@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
@@ -1,8 +1,263 @@
1
- import React from "react";
2
- import "./Accordion.module.css";
1
+ import React, { forwardRef, createContext, useContext, useState } from "react";
2
+ import {
3
+ Accordion as MuiAccordion,
4
+ AccordionSummary as MuiAccordionSummary,
5
+ AccordionDetails as MuiAccordionDetails,
6
+ type AccordionProps as MuiAccordionProps,
7
+ type AccordionSummaryProps as MuiAccordionSummaryProps,
8
+ type AccordionDetailsProps as MuiAccordionDetailsProps,
9
+ } from "@mui/material";
10
+ import {
11
+ filterStylingProps,
12
+ type RecursicaOverStyled,
13
+ } from "../../utils/filterStylingProps";
14
+ import styles from "./Accordion.module.css";
3
15
 
4
- export type AccordionProps = React.HTMLAttributes<HTMLDivElement>;
16
+ import {
17
+ type RecursicaAccordionProps,
18
+ type RecursicaAccordionItemProps,
19
+ type RecursicaAccordionControlProps,
20
+ } from "@recursica/adapter-common";
5
21
 
6
- export const Accordion: React.FC<AccordionProps> = (props) => {
7
- return <div {...props}>Accordion</div>;
22
+ const ChevronIcon = () => (
23
+ <svg
24
+ viewBox="0 0 24 24"
25
+ fill="none"
26
+ xmlns="http://www.w3.org/2000/svg"
27
+ stroke="currentColor"
28
+ strokeWidth="2"
29
+ strokeLinecap="round"
30
+ strokeLinejoin="round"
31
+ style={{ width: "100%", height: "100%" }}
32
+ >
33
+ <polyline points="6 9 12 15 18 9"></polyline>
34
+ </svg>
35
+ );
36
+
37
+ const AccordionContext = createContext<{
38
+ value: string | string[] | null;
39
+ onChange: (val: string) => void;
40
+ } | null>(null);
41
+
42
+ // ==== ACCORDION CONTAINER ====
43
+ export type AccordionProps = RecursicaOverStyled<
44
+ React.HTMLAttributes<HTMLDivElement> & RecursicaAccordionProps
45
+ >;
46
+
47
+ const AccordionBase = forwardRef<HTMLDivElement, AccordionProps>(
48
+ function Accordion(
49
+ {
50
+ variant = "unstyled",
51
+ overStyled = false,
52
+ value,
53
+ defaultValue,
54
+ onChange,
55
+ multiple = false,
56
+ children,
57
+ ...rest
58
+ },
59
+ ref,
60
+ ) {
61
+ const sanitizedProps = filterStylingProps(rest, overStyled);
62
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
63
+ .className as string | undefined;
64
+
65
+ const finalClass =
66
+ [styles.root, classNameProp].filter(Boolean).join(" ") || undefined;
67
+
68
+ const [uncontrolledValue, setUncontrolledValue] = useState<
69
+ string | string[] | null
70
+ >(defaultValue !== undefined ? defaultValue : multiple ? [] : null);
71
+ const activeValue = value !== undefined ? value : uncontrolledValue;
72
+
73
+ const handleItemChange = (itemValue: string) => {
74
+ let newValue: string | string[] | null;
75
+ if (multiple) {
76
+ const arr = Array.isArray(activeValue)
77
+ ? activeValue
78
+ : activeValue
79
+ ? [activeValue as string]
80
+ : [];
81
+ newValue = arr.includes(itemValue)
82
+ ? arr.filter((v) => v !== itemValue)
83
+ : [...arr, itemValue];
84
+ } else {
85
+ newValue = activeValue === itemValue ? null : itemValue;
86
+ }
87
+ setUncontrolledValue(newValue);
88
+ onChange?.(newValue);
89
+ };
90
+
91
+ return (
92
+ <AccordionContext.Provider
93
+ value={{ value: activeValue, onChange: handleItemChange }}
94
+ >
95
+ <div
96
+ ref={ref}
97
+ className={finalClass}
98
+ data-variant={variant}
99
+ {...(sanitizedProps as React.HTMLAttributes<HTMLDivElement>)}
100
+ >
101
+ {children}
102
+ </div>
103
+ </AccordionContext.Provider>
104
+ );
105
+ },
106
+ );
107
+ AccordionBase.displayName = "Accordion";
108
+
109
+ export type AccordionItemWrapperProps = RecursicaOverStyled<
110
+ Omit<MuiAccordionProps, "children" | "value"> & {
111
+ children?: React.ReactNode;
112
+ value: string;
113
+ } & RecursicaAccordionItemProps
114
+ >;
115
+
116
+ export const AccordionItem = forwardRef<
117
+ HTMLDivElement,
118
+ AccordionItemWrapperProps
119
+ >(function AccordionItem(
120
+ {
121
+ title,
122
+ leftIcon,
123
+ divider = true,
124
+ children,
125
+ value,
126
+ overStyled = false,
127
+ style,
128
+ ...rest
129
+ },
130
+ ref,
131
+ ) {
132
+ const sanitizedProps = filterStylingProps(rest, overStyled);
133
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
134
+ .className as string | undefined;
135
+
136
+ const finalClass =
137
+ [styles.item, divider ? undefined : styles.noDivider, classNameProp]
138
+ .filter(Boolean)
139
+ .join(" ") || undefined;
140
+
141
+ const ctx = useContext(AccordionContext);
142
+ const isExpanded = ctx
143
+ ? Array.isArray(ctx.value)
144
+ ? ctx.value.includes(value)
145
+ : ctx.value === value
146
+ : undefined;
147
+
148
+ const handleChange = () => {
149
+ ctx?.onChange(value);
150
+ };
151
+
152
+ // background-color: transparent allows Layer overrides to naturally penetrate
153
+ const mergedStyle = { backgroundColor: "transparent", ...style };
154
+
155
+ return (
156
+ <MuiAccordion
157
+ ref={ref}
158
+ className={finalClass}
159
+ disableGutters
160
+ elevation={0}
161
+ square
162
+ expanded={isExpanded}
163
+ onChange={handleChange}
164
+ style={mergedStyle}
165
+ {...(sanitizedProps as unknown as Omit<
166
+ MuiAccordionProps,
167
+ "expanded" | "onChange"
168
+ >)}
169
+ >
170
+ {
171
+ (title ? (
172
+ <>
173
+ <AccordionControl leftIcon={leftIcon}>{title}</AccordionControl>
174
+ <AccordionPanel>{children}</AccordionPanel>
175
+ </>
176
+ ) : (
177
+ children
178
+ )) as any
179
+ }
180
+ </MuiAccordion>
181
+ );
182
+ });
183
+ AccordionItem.displayName = "AccordionItem";
184
+
185
+ // ==== ACCORDION CONTROL ====
186
+ export type AccordionControlWrapperProps = RecursicaOverStyled<
187
+ MuiAccordionSummaryProps & RecursicaAccordionControlProps
188
+ >;
189
+
190
+ export const AccordionControl = forwardRef<
191
+ HTMLDivElement,
192
+ AccordionControlWrapperProps
193
+ >(function AccordionControl(
194
+ { leftIcon, children, overStyled = false, ...rest },
195
+ ref,
196
+ ) {
197
+ const sanitizedProps = filterStylingProps(rest, overStyled);
198
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
199
+ .className as string | undefined;
200
+
201
+ const finalClass =
202
+ [styles.control, classNameProp].filter(Boolean).join(" ") || undefined;
203
+
204
+ return (
205
+ <MuiAccordionSummary
206
+ ref={ref}
207
+ className={finalClass}
208
+ expandIcon={
209
+ <span className={styles.chevron}>
210
+ <ChevronIcon />
211
+ </span>
212
+ }
213
+ {...(sanitizedProps as unknown as MuiAccordionSummaryProps)}
214
+ >
215
+ {leftIcon && (
216
+ <span className={styles.iconLeftWrapper} aria-hidden>
217
+ {leftIcon}
218
+ </span>
219
+ )}
220
+ <div className={styles.label}>{children}</div>
221
+ </MuiAccordionSummary>
222
+ );
223
+ });
224
+ AccordionControl.displayName = "AccordionControl";
225
+
226
+ // ==== ACCORDION PANEL ====
227
+ export type AccordionPanelWrapperProps =
228
+ RecursicaOverStyled<MuiAccordionDetailsProps>;
229
+
230
+ export const AccordionPanel = forwardRef<
231
+ HTMLDivElement,
232
+ AccordionPanelWrapperProps
233
+ >(function AccordionPanel({ overStyled = false, children, ...rest }, ref) {
234
+ const sanitizedProps = filterStylingProps(rest, overStyled);
235
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
236
+ .className as string | undefined;
237
+
238
+ const finalClass =
239
+ [styles.panel, classNameProp].filter(Boolean).join(" ") || undefined;
240
+
241
+ return (
242
+ <MuiAccordionDetails
243
+ ref={ref}
244
+ className={finalClass}
245
+ {...(sanitizedProps as unknown as MuiAccordionDetailsProps)}
246
+ >
247
+ <div className={styles.content}>{children}</div>
248
+ </MuiAccordionDetails>
249
+ );
250
+ });
251
+ AccordionPanel.displayName = "AccordionPanel";
252
+
253
+ // ==== DOT NOTATION EXPORT ====
254
+ type AccordionComponent = typeof AccordionBase & {
255
+ Item: typeof AccordionItem;
256
+ Control: typeof AccordionControl;
257
+ Panel: typeof AccordionPanel;
8
258
  };
259
+
260
+ export const Accordion = AccordionBase as AccordionComponent;
261
+ Accordion.Item = AccordionItem;
262
+ Accordion.Control = AccordionControl;
263
+ Accordion.Panel = AccordionPanel;
@@ -1,20 +1,114 @@
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_assistive-element_properties_icon-size */
6
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_icon-text-gap */
7
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_max-width */
8
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_text_font-family */
9
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_text_font-size */
10
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_text_font-style */
11
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_text_font-weight */
12
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_text_letter-spacing */
13
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_text_line-height */
14
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_text_text-decoration */
15
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_text_text-transform */
16
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_properties_top-margin */
17
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_variants_types_error_properties_colors_icon-color */
18
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_variants_types_error_properties_colors_text-color */
19
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_variants_types_help_properties_colors_icon-color */
20
- /* recursica-ignore: --recursica_ui-kit_components_assistive-element_variants_types_help_properties_colors_text-color */
1
+ /*
2
+ ASSISTIVE ELEMENT ARCHITECTURE
3
+ This component strictly standardizes FormHelperText logic dynamically natively decoupled from the overarching input blocks.
4
+ */
5
+
6
+ .root {
7
+ /* Override MUI FormHelperText native resets structurally safely */
8
+ display: flex !important;
9
+ align-items: flex-start !important; /* Forces the icon to align with the first line of text */
10
+ margin: 0 !important; /* HARDCODE: structural reset */
11
+ padding: 0 !important; /* HARDCODE: structural reset */
12
+ width: 100%;
13
+
14
+ gap: var(
15
+ --recursica_ui-kit_components_assistive-element_properties_icon-text-gap
16
+ );
17
+ margin-top: var(
18
+ --recursica_ui-kit_components_assistive-element_properties_top-margin
19
+ ) !important;
20
+ max-width: var(
21
+ --recursica_ui-kit_components_assistive-element_properties_max-width
22
+ );
23
+
24
+ font-family: var(
25
+ --recursica_ui-kit_components_assistive-element_properties_text_font-family
26
+ );
27
+ font-size: var(
28
+ --recursica_ui-kit_components_assistive-element_properties_text_font-size
29
+ );
30
+ font-style: var(
31
+ --recursica_ui-kit_components_assistive-element_properties_text_font-style
32
+ );
33
+ font-weight: var(
34
+ --recursica_ui-kit_components_assistive-element_properties_text_font-weight
35
+ );
36
+ letter-spacing: var(
37
+ --recursica_ui-kit_components_assistive-element_properties_text_letter-spacing
38
+ );
39
+ line-height: var(
40
+ --recursica_ui-kit_components_assistive-element_properties_text_line-height
41
+ );
42
+ text-decoration: var(
43
+ --recursica_ui-kit_components_assistive-element_properties_text_text-decoration
44
+ );
45
+ text-transform: var(
46
+ --recursica_ui-kit_components_assistive-element_properties_text_text-transform
47
+ );
48
+ }
49
+
50
+ .text {
51
+ overflow-wrap: break-word;
52
+ white-space: normal;
53
+ }
54
+
55
+ /* Base Icon Architecture */
56
+ .icon {
57
+ flex-shrink: 0 !important; /* Prevent squishing */
58
+ display: flex !important;
59
+ align-items: center !important;
60
+ justify-content: center !important;
61
+
62
+ width: var(
63
+ --recursica_ui-kit_components_assistive-element_properties_icon-size
64
+ ) !important;
65
+ height: calc(
66
+ var(
67
+ --recursica_ui-kit_components_assistive-element_properties_text_line-height
68
+ ) *
69
+ var(
70
+ --recursica_ui-kit_components_assistive-element_properties_text_font-size
71
+ )
72
+ ) !important; /* Forces exact centering bounds within the first text line vertically */
73
+ }
74
+
75
+ .icon :global(svg) {
76
+ width: var(
77
+ --recursica_ui-kit_components_assistive-element_properties_icon-size
78
+ ) !important;
79
+ height: var(
80
+ --recursica_ui-kit_components_assistive-element_properties_icon-size
81
+ ) !important;
82
+ }
83
+
84
+ /* =========================================
85
+ VARIANTS: Default / Help
86
+ ========================================= */
87
+
88
+ .root[data-variant="help"] {
89
+ color: var(
90
+ --recursica_ui-kit_components_assistive-element_variants_types_help_properties_colors_text-color
91
+ );
92
+ }
93
+
94
+ .root[data-variant="help"] .icon {
95
+ color: var(
96
+ --recursica_ui-kit_components_assistive-element_variants_types_help_properties_colors_icon-color
97
+ );
98
+ }
99
+
100
+ /* =========================================
101
+ VARIANTS: Error
102
+ ========================================= */
103
+
104
+ .root[data-variant="error"] {
105
+ color: var(
106
+ --recursica_ui-kit_components_assistive-element_variants_types_error_properties_colors_text-color
107
+ );
108
+ }
109
+
110
+ .root[data-variant="error"] .icon {
111
+ color: var(
112
+ --recursica_ui-kit_components_assistive-element_variants_types_error_properties_colors_icon-color
113
+ );
114
+ }
@@ -1,35 +1,75 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { AssistiveElement } from "./AssistiveElement";
3
- import { ComingSoon } from "@recursica/storybook-template";
3
+ import { Layer } from "@recursica/adapter-common";
4
4
 
5
5
  const meta: Meta<typeof AssistiveElement> = {
6
- title: "UI-Kit/🚧 AssistiveElement",
6
+ title: "UI-Kit/AssistiveElement",
7
7
  component: AssistiveElement,
8
8
  tags: ["autodocs"],
9
9
  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
- ],
10
+ docs: {
11
+ description: {
12
+ component:
13
+ "The `AssistiveElement` is a semantic structural primitive designed to standardize Helper and Error descriptive blocks natively beneath form components globally. By explicitly wiring to the `--recursica_ui-kit_components_assistive-element` layout tokens, this component safely injects custom SVGs (Alerts vs Info circles) alongside constrained flex-wrapping typography strings, preserving flawless line-height and alignment logic entirely decoupled from underlying input engine frameworks.",
14
+ },
15
+ },
16
+ },
17
+ argTypes: {
18
+ assistiveVariant: {
19
+ control: "radio",
20
+ options: ["help", "error"],
21
+ },
22
+ assistiveWithIcon: {
23
+ control: "boolean",
26
24
  },
27
25
  },
28
26
  };
29
27
 
30
28
  export default meta;
29
+
31
30
  type Story = StoryObj<typeof AssistiveElement>;
32
31
 
33
- export const Default: Story = {
34
- render: () => <ComingSoon componentName="AssistiveElement" />,
32
+ export const DefaultHelp: Story = {
33
+ args: {
34
+ children:
35
+ "This is a standard assistive layout explaining specific configurations.",
36
+ assistiveVariant: "help",
37
+ assistiveWithIcon: true,
38
+ },
39
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
40
+ render: ({ withLayer, layer, ...args }: any) => (
41
+ <Layer layer={0} style={{ padding: "48px" }}>
42
+ <AssistiveElement {...args} />
43
+ </Layer>
44
+ ),
45
+ };
46
+
47
+ export const ErrorState: Story = {
48
+ args: {
49
+ children:
50
+ "Invalid property. You must satisfy the constraints outlined above.",
51
+ assistiveVariant: "error",
52
+ assistiveWithIcon: true,
53
+ },
54
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
55
+ render: ({ withLayer, layer, ...args }: any) => (
56
+ <Layer layer={0} style={{ padding: "48px" }}>
57
+ <AssistiveElement {...args} />
58
+ </Layer>
59
+ ),
60
+ };
61
+
62
+ export const NoIconHelp: Story = {
63
+ args: {
64
+ children:
65
+ "Fallback textual representation without visual injection targets.",
66
+ assistiveVariant: "help",
67
+ assistiveWithIcon: false,
68
+ },
69
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
70
+ render: ({ withLayer, layer, ...args }: any) => (
71
+ <Layer layer={0} style={{ padding: "48px" }}>
72
+ <AssistiveElement {...args} />
73
+ </Layer>
74
+ ),
35
75
  };
@@ -1,8 +1,84 @@
1
1
  import React from "react";
2
- import "./AssistiveElement.module.css";
2
+ import { FormHelperText, type FormHelperTextProps } from "@mui/material";
3
+ import { filterStylingProps } from "../../utils/filterStylingProps";
4
+ import styles from "./AssistiveElement.module.css";
3
5
 
4
- export type AssistiveElementProps = React.HTMLAttributes<HTMLDivElement>;
6
+ import { type RecursicaAssistiveElementProps } from "@recursica/adapter-common";
5
7
 
6
- export const AssistiveElement: React.FC<AssistiveElementProps> = (props) => {
7
- return <div {...props}>AssistiveElement</div>;
8
- };
8
+ export interface AssistiveElementProps
9
+ extends FormHelperTextProps,
10
+ RecursicaAssistiveElementProps {
11
+ overStyled?: boolean;
12
+ }
13
+
14
+ export const AssistiveElement = React.forwardRef<
15
+ HTMLParagraphElement,
16
+ AssistiveElementProps
17
+ >(function AssistiveElement(props, ref) {
18
+ const {
19
+ assistiveVariant = "help",
20
+ assistiveWithIcon = true,
21
+ overStyled = false,
22
+ className,
23
+ children,
24
+ ...rest
25
+ } = props;
26
+
27
+ const sanitizedProps = filterStylingProps(rest, overStyled);
28
+
29
+ const HelpIcon = () => (
30
+ <svg
31
+ xmlns="http://www.w3.org/2000/svg"
32
+ viewBox="0 0 24 24"
33
+ fill="none"
34
+ stroke="currentColor"
35
+ strokeWidth="2"
36
+ strokeLinecap="round"
37
+ strokeLinejoin="round"
38
+ >
39
+ <circle cx="12" cy="12" r="10" />
40
+ <path d="M12 16v-4" />
41
+ <path d="M12 8h.01" />
42
+ </svg>
43
+ );
44
+
45
+ const ErrorIcon = () => (
46
+ <svg
47
+ xmlns="http://www.w3.org/2000/svg"
48
+ viewBox="0 0 24 24"
49
+ fill="none"
50
+ stroke="currentColor"
51
+ strokeWidth="2"
52
+ strokeLinecap="round"
53
+ strokeLinejoin="round"
54
+ >
55
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
56
+ <path d="M12 9v4" />
57
+ <path d="M12 17h.01" />
58
+ </svg>
59
+ );
60
+
61
+ // Map to MUI's native error prop if it's explicitly an error state
62
+ const isError = assistiveVariant === "error";
63
+
64
+ // Force component = "div" so flex layouts execute correctly inside HelperText
65
+ return (
66
+ <FormHelperText
67
+ ref={ref}
68
+ component="div"
69
+ error={isError}
70
+ data-variant={assistiveVariant}
71
+ className={className ? `${styles.root} ${className}` : styles.root}
72
+ {...(sanitizedProps as FormHelperTextProps)}
73
+ >
74
+ {assistiveWithIcon && (
75
+ <div className={styles.icon}>
76
+ {assistiveVariant === "error" ? <ErrorIcon /> : <HelpIcon />}
77
+ </div>
78
+ )}
79
+ <div className={styles.text}>{children}</div>
80
+ </FormHelperText>
81
+ );
82
+ });
83
+
84
+ AssistiveElement.displayName = "AssistiveElement";