@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.
Files changed (212) hide show
  1. package/ARCHITECTURE.md +15 -0
  2. package/CHANGELOG.md +15 -0
  3. package/SETUP.md +77 -0
  4. package/USAGE.md +55 -0
  5. package/dist/mui-adapter.cjs +74 -1
  6. package/dist/mui-adapter.cjs.map +1 -1
  7. package/dist/mui-adapter.css +1 -1
  8. package/dist/mui-adapter.js +6863 -205
  9. package/dist/mui-adapter.js.map +1 -1
  10. package/dist/src/components/Accordion/Accordion.d.ts +43 -2
  11. package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +6 -2
  12. package/dist/src/components/Autocomplete/Autocomplete.d.ts +12 -2
  13. package/dist/src/components/Avatar/Avatar.d.ts +10 -3
  14. package/dist/src/components/Badge/Badge.d.ts +10 -3
  15. package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +9 -3
  16. package/dist/src/components/Button/Button.d.ts +2 -17
  17. package/dist/src/components/Card/Card.d.ts +38 -3
  18. package/dist/src/components/Checkbox/Checkbox.d.ts +13 -2
  19. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +18 -0
  20. package/dist/src/components/Checkbox/index.d.ts +5 -1
  21. package/dist/src/components/Chip/Chip.d.ts +9 -2
  22. package/dist/src/components/Container/Container.d.ts +1 -5
  23. package/dist/src/components/DatePicker/DatePicker.d.ts +2 -1
  24. package/dist/src/components/Dropdown/Dropdown.d.ts +12 -2
  25. package/dist/src/components/Flex/Flex.d.ts +2 -22
  26. package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +5 -2
  27. package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +9 -2
  28. package/dist/src/components/Group/Group.d.ts +2 -7
  29. package/dist/src/components/HoverCard/HoverCard.d.ts +28 -2
  30. package/dist/src/components/Label/Label.d.ts +6 -2
  31. package/dist/src/components/Link/Link.d.ts +2 -6
  32. package/dist/src/components/Loader/Loader.d.ts +2 -7
  33. package/dist/src/components/Menu/Menu.d.ts +25 -3
  34. package/dist/src/components/Modal/Modal.d.ts +37 -2
  35. package/dist/src/components/NumberInput/NumberInput.d.ts +12 -2
  36. package/dist/src/components/Pagination/Pagination.d.ts +12 -3
  37. package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
  38. package/dist/src/components/Panel/Panel.d.ts +52 -3
  39. package/dist/src/components/Radio/Radio.d.ts +13 -3
  40. package/dist/src/components/Radio/RadioGroup.d.ts +9 -0
  41. package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
  42. package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +4 -2
  43. package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
  44. package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
  45. package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
  46. package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
  47. package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +21 -3
  48. package/dist/src/components/Slider/Slider.d.ts +18 -2
  49. package/dist/src/components/Stack/Stack.d.ts +2 -5
  50. package/dist/src/components/Stepper/Stepper.d.ts +41 -2
  51. package/dist/src/components/Switch/Switch.d.ts +10 -3
  52. package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
  53. package/dist/src/components/Table/Table.d.ts +2 -1
  54. package/dist/src/components/Tabs/Tabs.d.ts +20 -3
  55. package/dist/src/components/Text/Text.d.ts +2 -16
  56. package/dist/src/components/TextArea/TextArea.d.ts +12 -2
  57. package/dist/src/components/TextField/TextField.d.ts +12 -2
  58. package/dist/src/components/TimePicker/TimePicker.d.ts +2 -1
  59. package/dist/src/components/Timeline/Timeline.d.ts +15 -2
  60. package/dist/src/components/Timeline/TimelineItem.d.ts +18 -0
  61. package/dist/src/components/Title/Title.d.ts +2 -15
  62. package/dist/src/components/Toast/Toast.d.ts +20 -2
  63. package/dist/src/components/Tooltip/Tooltip.d.ts +28 -3
  64. package/dist/src/components/TransferList/TransferList.d.ts +2 -1
  65. package/dist/src/components/Typography/Typography.d.ts +1 -1
  66. package/dist/src/utils/RequireAccessibleLabel.d.ts +1 -18
  67. package/dist/src/utils/filterStylingProps.d.ts +1 -11
  68. package/llms.txt +16 -0
  69. package/package.json +14 -3
  70. package/src/components/Accordion/Accordion.module.css +294 -56
  71. package/src/components/Accordion/Accordion.stories.tsx +131 -21
  72. package/src/components/Accordion/Accordion.tsx +260 -5
  73. package/src/components/AssistiveElement/AssistiveElement.module.css +114 -20
  74. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +60 -20
  75. package/src/components/AssistiveElement/AssistiveElement.tsx +81 -5
  76. package/src/components/Autocomplete/Autocomplete.module.css +319 -42
  77. package/src/components/Autocomplete/Autocomplete.stories.tsx +175 -23
  78. package/src/components/Autocomplete/Autocomplete.tsx +181 -6
  79. package/src/components/Avatar/Avatar.module.css +343 -73
  80. package/src/components/Avatar/Avatar.stories.tsx +94 -22
  81. package/src/components/Avatar/Avatar.tsx +99 -6
  82. package/src/components/Badge/Badge.module.css +127 -29
  83. package/src/components/Badge/Badge.stories.tsx +65 -22
  84. package/src/components/Badge/Badge.tsx +67 -6
  85. package/src/components/Breadcrumb/Breadcrumb.module.css +25 -5
  86. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
  87. package/src/components/Breadcrumb/Breadcrumb.tsx +59 -6
  88. package/src/components/Button/Button.module.css +19 -61
  89. package/src/components/Button/Button.stories.tsx +24 -23
  90. package/src/components/Button/Button.tsx +7 -18
  91. package/src/components/Card/Card.module.css +154 -38
  92. package/src/components/Card/Card.stories.tsx +117 -19
  93. package/src/components/Card/Card.tsx +181 -5
  94. package/src/components/Checkbox/Checkbox.module.css +249 -39
  95. package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
  96. package/src/components/Checkbox/Checkbox.tsx +246 -5
  97. package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
  98. package/src/components/Checkbox/CheckboxGroup.tsx +137 -0
  99. package/src/components/Checkbox/index.ts +6 -1
  100. package/src/components/Chip/Chip.module.css +246 -43
  101. package/src/components/Chip/Chip.stories.tsx +101 -21
  102. package/src/components/Chip/Chip.tsx +136 -6
  103. package/src/components/Container/Container.stories.tsx +103 -20
  104. package/src/components/Container/Container.tsx +1 -13
  105. package/src/components/DatePicker/DatePicker.stories.tsx +125 -19
  106. package/src/components/DatePicker/DatePicker.tsx +3 -1
  107. package/src/components/Dropdown/Dropdown.module.css +291 -41
  108. package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
  109. package/src/components/Dropdown/Dropdown.tsx +180 -6
  110. package/src/components/FileInput/FileInput.stories.tsx +2 -19
  111. package/src/components/FileUpload/FileUpload.stories.tsx +2 -19
  112. package/src/components/Flex/Flex.stories.tsx +113 -20
  113. package/src/components/Flex/Flex.tsx +16 -26
  114. package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
  115. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +67 -20
  116. package/src/components/FormControlLayout/FormControlLayout.tsx +69 -4
  117. package/src/components/FormControlWrapper/FormControlWrapper.module.css +87 -0
  118. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +116 -22
  119. package/src/components/FormControlWrapper/FormControlWrapper.tsx +126 -6
  120. package/src/components/Group/Group.stories.tsx +6 -30
  121. package/src/components/Group/Group.tsx +14 -9
  122. package/src/components/HoverCard/HoverCard.module.css +90 -22
  123. package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
  124. package/src/components/HoverCard/HoverCard.tsx +147 -5
  125. package/src/components/Label/Label.module.css +140 -35
  126. package/src/components/Label/Label.stories.tsx +108 -21
  127. package/src/components/Label/Label.tsx +82 -5
  128. package/src/components/Link/Link.module.css +4 -3
  129. package/src/components/Link/Link.stories.tsx +2 -8
  130. package/src/components/Link/Link.tsx +1 -5
  131. package/src/components/Loader/Loader.module.css +1 -2
  132. package/src/components/Loader/Loader.tsx +1 -7
  133. package/src/components/Menu/IMPLEMENTATION_NOTES.md +4 -0
  134. package/src/components/Menu/Menu.module.css +285 -51
  135. package/src/components/Menu/Menu.stories.tsx +229 -25
  136. package/src/components/Menu/Menu.tsx +73 -6
  137. package/src/components/Modal/Modal.module.css +171 -35
  138. package/src/components/Modal/Modal.stories.tsx +66 -22
  139. package/src/components/Modal/Modal.tsx +215 -5
  140. package/src/components/NumberInput/NumberInput.module.css +276 -42
  141. package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
  142. package/src/components/NumberInput/NumberInput.tsx +136 -6
  143. package/src/components/Pagination/IMPLEMENTATION_NOTES.md +3 -0
  144. package/src/components/Pagination/Pagination.icons.tsx +66 -0
  145. package/src/components/Pagination/Pagination.module.css +234 -20
  146. package/src/components/Pagination/Pagination.stories.tsx +34 -21
  147. package/src/components/Pagination/Pagination.tsx +74 -6
  148. package/src/components/Panel/Panel.module.css +206 -38
  149. package/src/components/Panel/Panel.stories.tsx +214 -22
  150. package/src/components/Panel/Panel.tsx +130 -5
  151. package/src/components/Radio/Radio.module.css +235 -37
  152. package/src/components/Radio/Radio.stories.tsx +77 -19
  153. package/src/components/Radio/Radio.tsx +210 -5
  154. package/src/components/Radio/RadioGroup.stories.tsx +117 -0
  155. package/src/components/Radio/RadioGroup.tsx +125 -0
  156. package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
  157. package/src/components/ReadOnlyField/ReadOnlyField.module.css +53 -15
  158. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +147 -19
  159. package/src/components/ReadOnlyField/ReadOnlyField.tsx +105 -5
  160. package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
  161. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
  162. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
  163. package/src/components/ReadOnlyField/index.ts +1 -0
  164. package/src/components/SegmentedControl/SegmentedControl.module.css +179 -47
  165. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
  166. package/src/components/SegmentedControl/SegmentedControl.tsx +140 -6
  167. package/src/components/Slider/Slider.module.css +539 -86
  168. package/src/components/Slider/Slider.stories.tsx +157 -19
  169. package/src/components/Slider/Slider.tsx +283 -5
  170. package/src/components/Stack/Stack.stories.tsx +4 -25
  171. package/src/components/Stack/Stack.tsx +1 -5
  172. package/src/components/Stepper/IMPLEMENTATION_NOTES.md +4 -0
  173. package/src/components/Stepper/Stepper.module.css +410 -71
  174. package/src/components/Stepper/Stepper.stories.tsx +146 -20
  175. package/src/components/Stepper/Stepper.tsx +147 -6
  176. package/src/components/Switch/Switch.module.css +259 -39
  177. package/src/components/Switch/Switch.stories.tsx +98 -19
  178. package/src/components/Switch/Switch.tsx +200 -6
  179. package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
  180. package/src/components/Switch/SwitchGroup.tsx +120 -0
  181. package/src/components/Table/Table.stories.tsx +1 -19
  182. package/src/components/Table/Table.tsx +3 -1
  183. package/src/components/Tabs/IMPLEMENTATION_NOTES.md +4 -0
  184. package/src/components/Tabs/Tabs.module.css +422 -79
  185. package/src/components/Tabs/Tabs.stories.tsx +156 -20
  186. package/src/components/Tabs/Tabs.tsx +86 -6
  187. package/src/components/Text/Text.stories.tsx +5 -17
  188. package/src/components/Text/Text.tsx +1 -15
  189. package/src/components/TextArea/TextArea.module.css +155 -32
  190. package/src/components/TextArea/TextArea.stories.tsx +80 -21
  191. package/src/components/TextArea/TextArea.tsx +146 -6
  192. package/src/components/TextField/TextField.module.css +261 -42
  193. package/src/components/TextField/TextField.stories.tsx +147 -20
  194. package/src/components/TextField/TextField.tsx +169 -6
  195. package/src/components/TimePicker/TimePicker.stories.tsx +2 -19
  196. package/src/components/TimePicker/TimePicker.tsx +3 -1
  197. package/src/components/Timeline/Timeline.module.css +365 -76
  198. package/src/components/Timeline/Timeline.stories.tsx +114 -21
  199. package/src/components/Timeline/Timeline.tsx +83 -5
  200. package/src/components/Timeline/TimelineItem.tsx +90 -0
  201. package/src/components/Title/Title.stories.tsx +0 -18
  202. package/src/components/Title/Title.tsx +1 -7
  203. package/src/components/Toast/Toast.module.css +166 -28
  204. package/src/components/Toast/Toast.stories.tsx +54 -22
  205. package/src/components/Toast/Toast.tsx +124 -5
  206. package/src/components/Tooltip/Tooltip.module.css +89 -24
  207. package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
  208. package/src/components/Tooltip/Tooltip.tsx +88 -5
  209. package/src/components/TransferList/TransferList.stories.tsx +1 -19
  210. package/src/components/TransferList/TransferList.tsx +3 -1
  211. package/src/utils/RequireAccessibleLabel.ts +1 -12
  212. package/src/utils/filterStylingProps.ts +7 -20
@@ -0,0 +1,48 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React from "react";
3
+ import { Box } from "@mui/material";
4
+ import {
5
+ filterStylingProps,
6
+ type RecursicaOverStyled,
7
+ } from "../../utils/filterStylingProps";
8
+ import styles from "./ReadOnlyField.module.css";
9
+
10
+ export interface RecursicaReadOnlyTextFieldProps {
11
+ /** The value strictly rendered as text output */
12
+ value?: any;
13
+ }
14
+
15
+ export type ReadOnlyTextFieldProps =
16
+ RecursicaOverStyled<RecursicaReadOnlyTextFieldProps>;
17
+
18
+ export const ReadOnlyTextField: React.FC<ReadOnlyTextFieldProps> = ({
19
+ value,
20
+ overStyled = false,
21
+ ...rest
22
+ }) => {
23
+ const sanitizedProps = filterStylingProps(rest, overStyled);
24
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
25
+ .className as string | undefined;
26
+
27
+ return (
28
+ <Box
29
+ component="p"
30
+ className={
31
+ classNameProp
32
+ ? `${styles.textField} ${classNameProp}`
33
+ : styles.textField
34
+ }
35
+ {...sanitizedProps}
36
+ >
37
+ {value != null
38
+ ? React.isValidElement(value)
39
+ ? value
40
+ : Array.isArray(value)
41
+ ? value.join(", ")
42
+ : String(value)
43
+ : ""}
44
+ </Box>
45
+ );
46
+ };
47
+
48
+ ReadOnlyTextField.displayName = "ReadOnlyTextField";
@@ -0,0 +1,93 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React, { forwardRef } from "react";
3
+ import { type ReadOnlyFieldType } from "@recursica/adapter-common";
4
+ import {
5
+ FormControlWrapper,
6
+ type FormControlWrapperProps,
7
+ } from "../FormControlWrapper/FormControlWrapper";
8
+ import { ReadOnlyField } from "./ReadOnlyField";
9
+
10
+ export interface WithReadOnlyWrapperProps
11
+ extends Omit<FormControlWrapperProps, "children" | "overStyled"> {
12
+ /** Injects whether the field defaults to reading mode natively */
13
+ readOnly?: boolean;
14
+ /** Explicit React component directly overtaking the baseline text renderer when read-only mode is active */
15
+ readOnlyComponent?: React.ElementType<any>;
16
+ /** Custom renderer explicitly responsible for formatting missing/empty value mappings (overriding default 'N/A') */
17
+ emptyValueComponent?: React.ElementType<{ value?: any }>;
18
+ /** The full native props dictionary securely forwarded into the custom readOnlyComponent if active */
19
+ readOnlyNativeProps?: any;
20
+ /** Instructs the underlying generic ReadOnlyField which raw visual structure to bridge automatically */
21
+ readOnlyType?: ReadOnlyFieldType;
22
+ /** The isolated raw data value intercepted explicitly into the ReadOnly formatters */
23
+ readOnlyValue?: any;
24
+ /** Safely enables deep overrides transparently bypassing block filters */
25
+ overStyled?: boolean;
26
+ /** The nested active node dynamically exposed exclusively when read-only bounds are disabled */
27
+ activeComponent: React.ReactNode;
28
+ }
29
+
30
+ /**
31
+ * Universal execution barrier trapping readOnly states globally.
32
+ * Natively delegates active logic down avoiding duplicative internal component bindings dynamically.
33
+ */
34
+ export const WithReadOnlyWrapper = forwardRef<
35
+ HTMLDivElement,
36
+ WithReadOnlyWrapperProps
37
+ >(function WithReadOnlyWrapper(
38
+ {
39
+ readOnly,
40
+ readOnlyComponent,
41
+ readOnlyType = "text",
42
+ readOnlyValue,
43
+ readOnlyNativeProps,
44
+ emptyValueComponent,
45
+ activeComponent,
46
+ overStyled,
47
+ onLabelEditClick,
48
+ ...wrapperProps
49
+ },
50
+ ref,
51
+ ) {
52
+ if (readOnly) {
53
+ if (readOnlyComponent) {
54
+ const ReadOnlyComponent = readOnlyComponent;
55
+ return (
56
+ <FormControlWrapper
57
+ ref={ref}
58
+ {...wrapperProps}
59
+ onLabelEditClick={onLabelEditClick}
60
+ overStyled={overStyled as true}
61
+ >
62
+ <ReadOnlyComponent {...readOnlyNativeProps} />
63
+ </FormControlWrapper>
64
+ );
65
+ }
66
+
67
+ return (
68
+ <ReadOnlyField
69
+ ref={ref}
70
+ type={readOnlyType}
71
+ value={readOnlyValue}
72
+ emptyValueComponent={emptyValueComponent}
73
+ onLabelEditClick={onLabelEditClick}
74
+ overStyled={overStyled as true}
75
+ {...wrapperProps}
76
+ />
77
+ );
78
+ }
79
+
80
+ // Active Standard Execution natively forwarding bound children implicitly
81
+ return (
82
+ <FormControlWrapper
83
+ ref={ref}
84
+ {...wrapperProps}
85
+ onLabelEditClick={onLabelEditClick}
86
+ overStyled={overStyled as true}
87
+ >
88
+ {activeComponent}
89
+ </FormControlWrapper>
90
+ );
91
+ });
92
+
93
+ WithReadOnlyWrapper.displayName = "WithReadOnlyWrapper";
@@ -1 +1,2 @@
1
1
  export * from "./ReadOnlyField";
2
+ export * from "./WithReadOnlyWrapper";
@@ -1,47 +1,179 @@
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_segmented-control-item_properties_item_border-radius */
6
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_item_height */
7
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_item_icon-size */
8
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_item_icon-text-gap */
9
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_item_padding-horizontal */
10
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected-text_font-family */
11
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected-text_font-size */
12
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected-text_font-style */
13
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected-text_font-weight */
14
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected-text_letter-spacing */
15
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected-text_line-height */
16
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected-text_text-decoration */
17
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected-text_text-transform */
18
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected_border-radius */
19
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected_border-size */
20
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected_colors_background */
21
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected_colors_border-color */
22
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected_colors_text-color */
23
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_selected_elevation */
24
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_font-family */
25
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_font-size */
26
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_font-style */
27
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_font-weight */
28
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_letter-spacing */
29
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_line-height */
30
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_text-decoration */
31
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_text-transform */
32
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected_border-radius */
33
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected_border-size */
34
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected_colors_background */
35
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected_colors_border-color */
36
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected_colors_text-color */
37
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control-item_properties_unselected_elevation */
38
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_border-radius */
39
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_border-size */
40
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_colors_background */
41
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_colors_border-color */
42
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_colors_divider-color */
43
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_divider-size */
44
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_elevation */
45
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_item-gap */
46
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_padding-horizontal */
47
- /* recursica-ignore: --recursica_ui-kit_components_segmented-control_properties_padding-vertical */
1
+ /* HARDCODED VALUES:
2
+ - border-style: solid; on container and indicator
3
+ - background-color: transparent; on label hover (overriding Mantine)
4
+ - Scope prefix .root to enforce Figma tokens over Mantine's inline calculation without using !important
5
+ */
6
+
7
+ .root {
8
+ background-color: var(
9
+ --recursica_ui-kit_components_segmented-control_properties_colors_background
10
+ );
11
+ border: var(
12
+ --recursica_ui-kit_components_segmented-control_properties_border-size
13
+ )
14
+ solid
15
+ var(
16
+ --recursica_ui-kit_components_segmented-control_properties_colors_border-color
17
+ );
18
+ border-radius: var(
19
+ --recursica_ui-kit_components_segmented-control_properties_border-radius
20
+ );
21
+ padding: var(
22
+ --recursica_ui-kit_components_segmented-control_properties_padding-vertical
23
+ )
24
+ var(
25
+ --recursica_ui-kit_components_segmented-control_properties_padding-horizontal
26
+ );
27
+ box-shadow: var(
28
+ --recursica_ui-kit_components_segmented-control_properties_elevation
29
+ );
30
+ gap: var(--recursica_ui-kit_components_segmented-control_properties_item-gap);
31
+ }
32
+
33
+ .root .label {
34
+ padding-left: var(
35
+ --recursica_ui-kit_components_segmented-control-item_properties_item_padding-horizontal
36
+ );
37
+ padding-right: var(
38
+ --recursica_ui-kit_components_segmented-control-item_properties_item_padding-horizontal
39
+ );
40
+ height: var(
41
+ --recursica_ui-kit_components_segmented-control-item_properties_item_height
42
+ );
43
+ border-radius: var(
44
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected_border-radius
45
+ );
46
+
47
+ border: var(
48
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected_border-size
49
+ )
50
+ solid
51
+ var(
52
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected_colors_border-color
53
+ );
54
+ background-color: var(
55
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected_colors_background
56
+ );
57
+ box-shadow: var(
58
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected_elevation
59
+ );
60
+
61
+ font-family: var(
62
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_font-family
63
+ );
64
+ font-size: var(
65
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_font-size
66
+ );
67
+ font-style: var(
68
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_font-style
69
+ );
70
+ font-weight: var(
71
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_font-weight
72
+ );
73
+ letter-spacing: var(
74
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_letter-spacing
75
+ );
76
+ line-height: var(
77
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_line-height
78
+ );
79
+ text-decoration: var(
80
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_text-decoration
81
+ );
82
+ text-transform: var(
83
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected-text_text-transform
84
+ );
85
+
86
+ color: var(
87
+ --recursica_ui-kit_components_segmented-control-item_properties_unselected_colors_text-color
88
+ );
89
+
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ }
94
+
95
+ /* Mantine natively wraps label contents in an innerLabel span */
96
+ .root .label :global(.mantine-SegmentedControl-innerLabel) {
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ gap: var(
101
+ --recursica_ui-kit_components_segmented-control-item_properties_item_icon-text-gap
102
+ );
103
+ }
104
+
105
+ .root .label svg {
106
+ width: var(
107
+ --recursica_ui-kit_components_segmented-control-item_properties_item_icon-size
108
+ );
109
+ height: var(
110
+ --recursica_ui-kit_components_segmented-control-item_properties_item_icon-size
111
+ );
112
+ flex-shrink: 0;
113
+ }
114
+
115
+ /* Mantine adds a background on hover, we must strictly strip it */
116
+ .root .label:hover {
117
+ background-color: transparent;
118
+ }
119
+
120
+ /* Mantine adds separators via ::before on the .control */
121
+ .root .control::before {
122
+ background-color: var(
123
+ --recursica_ui-kit_components_segmented-control_properties_colors_divider-color
124
+ );
125
+ width: var(
126
+ --recursica_ui-kit_components_segmented-control_properties_divider-size
127
+ );
128
+ }
129
+
130
+ /* Indicator mapping (MUI ToggleButton applies .Mui-selected to the button instead of rendering a sliding indicator) */
131
+ .root .control.Mui-selected {
132
+ background-color: var(
133
+ --recursica_ui-kit_components_segmented-control-item_properties_selected_colors_background
134
+ );
135
+ border: var(
136
+ --recursica_ui-kit_components_segmented-control-item_properties_selected_border-size
137
+ )
138
+ solid
139
+ var(
140
+ --recursica_ui-kit_components_segmented-control-item_properties_selected_colors_border-color
141
+ );
142
+ box-shadow: var(
143
+ --recursica_ui-kit_components_segmented-control-item_properties_selected_elevation
144
+ );
145
+ border-radius: var(
146
+ --recursica_ui-kit_components_segmented-control-item_properties_selected_border-radius
147
+ );
148
+ }
149
+
150
+ /* Selected label text color override */
151
+ .root .control.Mui-selected .label {
152
+ color: var(
153
+ --recursica_ui-kit_components_segmented-control-item_properties_selected_colors_text-color
154
+ );
155
+ font-family: var(
156
+ --recursica_ui-kit_components_segmented-control-item_properties_selected-text_font-family
157
+ );
158
+ font-size: var(
159
+ --recursica_ui-kit_components_segmented-control-item_properties_selected-text_font-size
160
+ );
161
+ font-style: var(
162
+ --recursica_ui-kit_components_segmented-control-item_properties_selected-text_font-style
163
+ );
164
+ font-weight: var(
165
+ --recursica_ui-kit_components_segmented-control-item_properties_selected-text_font-weight
166
+ );
167
+ letter-spacing: var(
168
+ --recursica_ui-kit_components_segmented-control-item_properties_selected-text_letter-spacing
169
+ );
170
+ line-height: var(
171
+ --recursica_ui-kit_components_segmented-control-item_properties_selected-text_line-height
172
+ );
173
+ text-decoration: var(
174
+ --recursica_ui-kit_components_segmented-control-item_properties_selected-text_text-decoration
175
+ );
176
+ text-transform: var(
177
+ --recursica_ui-kit_components_segmented-control-item_properties_selected-text_text-transform
178
+ );
179
+ }
@@ -1,35 +1,117 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { SegmentedControl } from "./SegmentedControl";
3
- import { ComingSoon } from "@recursica/storybook-template";
4
3
 
5
4
  const meta: Meta<typeof SegmentedControl> = {
6
- title: "UI-Kit/🚧 SegmentedControl",
5
+ title: "UI-Kit/SegmentedControl",
7
6
  component: SegmentedControl,
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
+ "SegmentedControl provides a linear set of two or more segments, each of which functions as a mutually exclusive button.",
13
+ },
26
14
  },
27
15
  },
28
- };
16
+ argTypes: {
17
+ orientation: {
18
+ control: "radio",
19
+ options: ["horizontal", "vertical"],
20
+ },
21
+ fullWidth: {
22
+ control: "boolean",
23
+ },
24
+ disabled: { table: { disable: true } },
25
+ data: { table: { disable: true } },
26
+ defaultChecked: { table: { disable: true } },
27
+ },
28
+ } satisfies Meta<typeof SegmentedControl>;
29
29
 
30
30
  export default meta;
31
31
  type Story = StoryObj<typeof SegmentedControl>;
32
32
 
33
33
  export const Default: Story = {
34
- render: () => <ComingSoon componentName="SegmentedControl" />,
34
+ args: {
35
+ data: ["React", "Angular", "Vue", "Svelte"],
36
+ orientation: "horizontal",
37
+ fullWidth: false,
38
+ },
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
40
+ render: ({ withLayer, layer, ...args }: any) => {
41
+ return <SegmentedControl {...args} />;
42
+ },
43
+ };
44
+
45
+ export const FullWidth: Story = {
46
+ args: {
47
+ data: ["Daily", "Weekly", "Monthly"],
48
+ fullWidth: true,
49
+ },
50
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
51
+ render: ({ withLayer, layer, ...args }: any) => {
52
+ return <SegmentedControl {...args} />;
53
+ },
54
+ };
55
+
56
+ export const Vertical: Story = {
57
+ args: {
58
+ data: ["Option 1", "Option 2", "Option 3"],
59
+ orientation: "vertical",
60
+ },
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
62
+ render: ({ withLayer, layer, ...args }: any) => {
63
+ return <SegmentedControl {...args} />;
64
+ },
65
+ };
66
+
67
+ const CheckIcon = () => (
68
+ <svg
69
+ viewBox="0 0 24 24"
70
+ fill="none"
71
+ xmlns="http://www.w3.org/2000/svg"
72
+ stroke="currentColor"
73
+ strokeWidth="2"
74
+ strokeLinecap="round"
75
+ strokeLinejoin="round"
76
+ >
77
+ <polyline points="20 6 9 17 4 12" />
78
+ </svg>
79
+ );
80
+
81
+ export const WithIcons: Story = {
82
+ args: {
83
+ data: [
84
+ {
85
+ value: "daily",
86
+ label: (
87
+ <>
88
+ <CheckIcon />
89
+ <span>Daily</span>
90
+ </>
91
+ ),
92
+ },
93
+ {
94
+ value: "weekly",
95
+ label: (
96
+ <>
97
+ <CheckIcon />
98
+ <span>Weekly</span>
99
+ </>
100
+ ),
101
+ },
102
+ {
103
+ value: "monthly",
104
+ label: (
105
+ <>
106
+ <CheckIcon />
107
+ <span>Monthly</span>
108
+ </>
109
+ ),
110
+ },
111
+ ],
112
+ },
113
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
114
+ render: ({ withLayer, layer, ...args }: any) => {
115
+ return <SegmentedControl {...args} />;
116
+ },
35
117
  };
@@ -1,8 +1,142 @@
1
- import React from "react";
2
- import "./SegmentedControl.module.css";
1
+ import { forwardRef } from "react";
2
+ import {
3
+ ToggleButtonGroup as MuiSegmentedControl,
4
+ ToggleButton,
5
+ type ToggleButtonGroupProps as MuiSegmentedControlProps,
6
+ } from "@mui/material";
7
+ import {
8
+ filterStylingProps,
9
+ type RecursicaOverStyled,
10
+ } from "../../utils/filterStylingProps";
11
+ import styles from "./SegmentedControl.module.css";
3
12
 
4
- export type SegmentedControlProps = React.HTMLAttributes<HTMLDivElement>;
13
+ import { type RecursicaSegmentedControlProps } from "@recursica/adapter-common";
5
14
 
6
- export const SegmentedControl: React.FC<SegmentedControlProps> = (props) => {
7
- return <div {...props}>SegmentedControl</div>;
8
- };
15
+ export type SegmentedControlProps = RecursicaOverStyled<
16
+ Omit<
17
+ MuiSegmentedControlProps,
18
+ | "variant"
19
+ | "size"
20
+ | "radius"
21
+ | "color"
22
+ | "classNames"
23
+ | "className"
24
+ | "disabled"
25
+ | "value"
26
+ | "onChange"
27
+ > & {
28
+ className?: string;
29
+ classNames?: Partial<Record<string, string>>;
30
+ } & RecursicaSegmentedControlProps
31
+ >;
32
+
33
+ function useSegmentedControlClassNames(restRecord: Record<string, unknown>): {
34
+ className: string;
35
+ classNames: Partial<Record<string, string>>;
36
+ } {
37
+ const mergedClassNames: Partial<Record<string, string>> = {
38
+ root: styles.root,
39
+ control: styles.control,
40
+ label: styles.label,
41
+ indicator: styles.indicator,
42
+ };
43
+
44
+ const classNamesProp = restRecord.classNames;
45
+ if (
46
+ classNamesProp &&
47
+ typeof classNamesProp === "object" &&
48
+ !Array.isArray(classNamesProp)
49
+ ) {
50
+ const o = classNamesProp as Partial<Record<string, string>>;
51
+ mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
52
+ mergedClassNames.control = o.control
53
+ ? `${styles.control} ${o.control}`
54
+ : styles.control;
55
+ mergedClassNames.label = o.label
56
+ ? `${styles.label} ${o.label}`
57
+ : styles.label;
58
+ mergedClassNames.indicator = o.indicator
59
+ ? `${styles.indicator} ${o.indicator}`
60
+ : styles.indicator;
61
+ }
62
+
63
+ const classNameProp = restRecord.className as string | undefined;
64
+ const finalClass = classNameProp
65
+ ? `${styles.root} ${classNameProp}`
66
+ : styles.root;
67
+
68
+ return { className: finalClass, classNames: mergedClassNames };
69
+ }
70
+
71
+ const _SegmentedControl = forwardRef<HTMLDivElement, SegmentedControlProps>(
72
+ function SegmentedControl(
73
+ {
74
+ overStyled = false,
75
+ orientation = "horizontal",
76
+ fullWidth,
77
+ data = [],
78
+ value,
79
+ onChange,
80
+ ...rest
81
+ },
82
+ ref,
83
+ ) {
84
+ const sanitizedProps = filterStylingProps(rest, overStyled);
85
+ const restRecord = sanitizedProps as Record<string, unknown>;
86
+
87
+ // Explicitly prevent consumers from bypassing the disabled restriction
88
+ delete restRecord["disabled"];
89
+
90
+ const stylingParams = useSegmentedControlClassNames(restRecord);
91
+
92
+ const handleChange = (
93
+ _event: React.MouseEvent<HTMLElement>,
94
+ newValue: string | null,
95
+ ) => {
96
+ if (newValue !== null && onChange) {
97
+ onChange(newValue);
98
+ }
99
+ };
100
+
101
+ return (
102
+ <MuiSegmentedControl
103
+ ref={ref}
104
+ className={stylingParams.className}
105
+ classes={stylingParams.classNames}
106
+ orientation={orientation}
107
+ fullWidth={fullWidth}
108
+ data-orientation={orientation}
109
+ exclusive
110
+ value={value}
111
+ onChange={handleChange as any}
112
+ {...(sanitizedProps as Omit<
113
+ MuiSegmentedControlProps,
114
+ "variant" | "size" | "value" | "onChange"
115
+ >)}
116
+ >
117
+ {data.map((item) => {
118
+ const itemValue = typeof item === "string" ? item : item.value;
119
+ const itemLabel = typeof item === "string" ? item : item.label;
120
+ const itemDisabled = typeof item === "string" ? false : item.disabled;
121
+
122
+ return (
123
+ <ToggleButton
124
+ key={itemValue}
125
+ value={itemValue}
126
+ disabled={itemDisabled}
127
+ className={stylingParams.classNames.control}
128
+ >
129
+ <div className={stylingParams.classNames.label}>{itemLabel}</div>
130
+ </ToggleButton>
131
+ );
132
+ })}
133
+ </MuiSegmentedControl>
134
+ );
135
+ },
136
+ );
137
+ _SegmentedControl.displayName = "SegmentedControl";
138
+
139
+ /**
140
+ * Recursica SegmentedControl component wrapping Mui's SegmentedControl.
141
+ */
142
+ export const SegmentedControl = _SegmentedControl;