@recursica/mui-adapter 0.5.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 (159) hide show
  1. package/CHANGELOG.md +9 -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 +6652 -204
  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 +16 -2
  10. package/dist/src/components/Avatar/Avatar.d.ts +14 -2
  11. package/dist/src/components/Badge/Badge.d.ts +13 -3
  12. package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +10 -3
  13. package/dist/src/components/Button/Button.d.ts +1 -1
  14. package/dist/src/components/Card/Card.d.ts +40 -3
  15. package/dist/src/components/Checkbox/Checkbox.d.ts +15 -2
  16. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +16 -0
  17. package/dist/src/components/Checkbox/index.d.ts +5 -1
  18. package/dist/src/components/Chip/Chip.d.ts +18 -2
  19. package/dist/src/components/Dropdown/Dropdown.d.ts +23 -3
  20. package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +10 -2
  21. package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +24 -2
  22. package/dist/src/components/HoverCard/HoverCard.d.ts +55 -3
  23. package/dist/src/components/Label/Label.d.ts +13 -2
  24. package/dist/src/components/Link/Link.d.ts +1 -1
  25. package/dist/src/components/Menu/Menu.d.ts +99 -3
  26. package/dist/src/components/Modal/Modal.d.ts +36 -2
  27. package/dist/src/components/NumberInput/NumberInput.d.ts +14 -2
  28. package/dist/src/components/Pagination/Pagination.d.ts +44 -2
  29. package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
  30. package/dist/src/components/Panel/Panel.d.ts +65 -3
  31. package/dist/src/components/Radio/Radio.d.ts +13 -3
  32. package/dist/src/components/Radio/RadioGroup.d.ts +13 -0
  33. package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
  34. package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +7 -2
  35. package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
  36. package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
  37. package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
  38. package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
  39. package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +31 -3
  40. package/dist/src/components/Slider/Slider.d.ts +28 -2
  41. package/dist/src/components/Stepper/Stepper.d.ts +21 -2
  42. package/dist/src/components/Switch/Switch.d.ts +10 -3
  43. package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
  44. package/dist/src/components/Tabs/Tabs.d.ts +35 -3
  45. package/dist/src/components/Text/Text.d.ts +1 -1
  46. package/dist/src/components/TextArea/TextArea.d.ts +18 -2
  47. package/dist/src/components/TextField/TextField.d.ts +16 -2
  48. package/dist/src/components/Timeline/Timeline.d.ts +14 -2
  49. package/dist/src/components/Timeline/TimelineItem.d.ts +29 -0
  50. package/dist/src/components/Title/Title.d.ts +1 -1
  51. package/dist/src/components/Toast/Toast.d.ts +25 -2
  52. package/dist/src/components/Tooltip/Tooltip.d.ts +42 -3
  53. package/dist/src/components/Typography/Typography.d.ts +1 -1
  54. package/package.json +9 -2
  55. package/src/components/Accordion/Accordion.module.css +295 -56
  56. package/src/components/Accordion/Accordion.stories.tsx +149 -21
  57. package/src/components/Accordion/Accordion.tsx +264 -5
  58. package/src/components/AssistiveElement/AssistiveElement.module.css +111 -20
  59. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +76 -19
  60. package/src/components/AssistiveElement/AssistiveElement.tsx +85 -5
  61. package/src/components/Autocomplete/Autocomplete.module.css +320 -42
  62. package/src/components/Autocomplete/Autocomplete.stories.tsx +180 -19
  63. package/src/components/Autocomplete/Autocomplete.tsx +199 -6
  64. package/src/components/Avatar/Avatar.module.css +343 -73
  65. package/src/components/Avatar/Avatar.stories.tsx +94 -22
  66. package/src/components/Avatar/Avatar.tsx +110 -6
  67. package/src/components/Badge/Badge.module.css +127 -29
  68. package/src/components/Badge/Badge.stories.tsx +65 -22
  69. package/src/components/Badge/Badge.tsx +70 -6
  70. package/src/components/Breadcrumb/Breadcrumb.module.css +25 -5
  71. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
  72. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -6
  73. package/src/components/Button/Button.module.css +3 -3
  74. package/src/components/Card/Card.module.css +118 -38
  75. package/src/components/Card/Card.stories.tsx +117 -19
  76. package/src/components/Card/Card.tsx +179 -5
  77. package/src/components/Checkbox/Checkbox.module.css +232 -39
  78. package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
  79. package/src/components/Checkbox/Checkbox.tsx +146 -5
  80. package/src/components/Checkbox/CheckboxGroup.tsx +117 -0
  81. package/src/components/Checkbox/index.ts +6 -1
  82. package/src/components/Chip/Chip.module.css +228 -43
  83. package/src/components/Chip/Chip.stories.tsx +101 -21
  84. package/src/components/Chip/Chip.tsx +138 -6
  85. package/src/components/Dropdown/Dropdown.module.css +291 -41
  86. package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
  87. package/src/components/Dropdown/Dropdown.tsx +182 -6
  88. package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
  89. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +74 -19
  90. package/src/components/FormControlLayout/FormControlLayout.tsx +58 -4
  91. package/src/components/FormControlWrapper/FormControlWrapper.module.css +96 -0
  92. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +122 -19
  93. package/src/components/FormControlWrapper/FormControlWrapper.tsx +145 -6
  94. package/src/components/HoverCard/HoverCard.module.css +90 -22
  95. package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
  96. package/src/components/HoverCard/HoverCard.tsx +150 -5
  97. package/src/components/Label/Label.module.css +136 -35
  98. package/src/components/Label/Label.stories.tsx +98 -17
  99. package/src/components/Label/Label.tsx +88 -5
  100. package/src/components/Menu/Menu.module.css +285 -51
  101. package/src/components/Menu/Menu.stories.tsx +307 -21
  102. package/src/components/Menu/Menu.tsx +318 -5
  103. package/src/components/Modal/Modal.module.css +171 -35
  104. package/src/components/Modal/Modal.stories.tsx +66 -22
  105. package/src/components/Modal/Modal.tsx +216 -4
  106. package/src/components/NumberInput/NumberInput.module.css +276 -42
  107. package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
  108. package/src/components/NumberInput/NumberInput.tsx +148 -6
  109. package/src/components/Pagination/Pagination.icons.tsx +66 -0
  110. package/src/components/Pagination/Pagination.module.css +246 -20
  111. package/src/components/Pagination/Pagination.stories.tsx +42 -21
  112. package/src/components/Pagination/Pagination.tsx +193 -5
  113. package/src/components/Panel/Panel.module.css +206 -38
  114. package/src/components/Panel/Panel.stories.tsx +214 -22
  115. package/src/components/Panel/Panel.tsx +153 -5
  116. package/src/components/Radio/Radio.module.css +220 -37
  117. package/src/components/Radio/Radio.stories.tsx +77 -19
  118. package/src/components/Radio/Radio.tsx +178 -5
  119. package/src/components/Radio/RadioGroup.tsx +111 -0
  120. package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
  121. package/src/components/ReadOnlyField/ReadOnlyField.module.css +53 -15
  122. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +175 -16
  123. package/src/components/ReadOnlyField/ReadOnlyField.tsx +109 -5
  124. package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
  125. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
  126. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
  127. package/src/components/ReadOnlyField/index.ts +1 -0
  128. package/src/components/SegmentedControl/SegmentedControl.module.css +203 -47
  129. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
  130. package/src/components/SegmentedControl/SegmentedControl.tsx +110 -6
  131. package/src/components/Slider/Slider.module.css +542 -86
  132. package/src/components/Slider/Slider.stories.tsx +157 -19
  133. package/src/components/Slider/Slider.tsx +282 -5
  134. package/src/components/Stepper/Stepper.module.css +410 -71
  135. package/src/components/Stepper/Stepper.stories.tsx +132 -19
  136. package/src/components/Stepper/Stepper.tsx +145 -6
  137. package/src/components/Switch/Switch.module.css +238 -39
  138. package/src/components/Switch/Switch.stories.tsx +98 -19
  139. package/src/components/Switch/Switch.tsx +153 -6
  140. package/src/components/Switch/SwitchGroup.tsx +111 -0
  141. package/src/components/Tabs/Tabs.module.css +422 -79
  142. package/src/components/Tabs/Tabs.stories.tsx +138 -19
  143. package/src/components/Tabs/Tabs.tsx +103 -6
  144. package/src/components/TextArea/TextArea.module.css +155 -32
  145. package/src/components/TextArea/TextArea.stories.tsx +80 -21
  146. package/src/components/TextArea/TextArea.tsx +156 -6
  147. package/src/components/TextField/TextField.module.css +261 -42
  148. package/src/components/TextField/TextField.stories.tsx +168 -15
  149. package/src/components/TextField/TextField.tsx +171 -6
  150. package/src/components/Timeline/Timeline.module.css +365 -76
  151. package/src/components/Timeline/Timeline.stories.tsx +114 -21
  152. package/src/components/Timeline/Timeline.tsx +80 -5
  153. package/src/components/Timeline/TimelineItem.tsx +101 -0
  154. package/src/components/Toast/Toast.module.css +166 -28
  155. package/src/components/Toast/Toast.stories.tsx +54 -22
  156. package/src/components/Toast/Toast.tsx +90 -5
  157. package/src/components/Tooltip/Tooltip.module.css +89 -24
  158. package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
  159. package/src/components/Tooltip/Tooltip.tsx +114 -5
@@ -1,8 +1,321 @@
1
- import React from "react";
2
- import "./Menu.module.css";
1
+ import { forwardRef } from "react";
2
+ import { Menu as MuiMenu, type MenuProps as MuiMenuProps } from "@mui/material";
3
+ import {
4
+ filterStylingProps,
5
+ type RecursicaOverStyled,
6
+ } from "../../utils/filterStylingProps";
7
+ import styles from "./Menu.module.css";
3
8
 
4
- export type MenuProps = React.HTMLAttributes<HTMLDivElement>;
9
+ // ============================================================
10
+ // MENU ROOT
11
+ // ============================================================
5
12
 
6
- export const Menu: React.FC<MenuProps> = (props) => {
7
- return <div {...props}>Menu</div>;
13
+ /**
14
+ * Recursica Menu component wrapping Mui's composable Menu.
15
+ *
16
+ * Usage follows the composable dot-notation pattern:
17
+ * ```tsx
18
+ * <Menu>
19
+ * <Menu.Target><Button>Open</Button></Menu.Target>
20
+ * <Menu.Dropdown>
21
+ * <Menu.Label>Section</Menu.Label>
22
+ * <Menu.Item>Action</Menu.Item>
23
+ * <Menu.Divider />
24
+ * <Menu.Item>Another action</Menu.Item>
25
+ * </Menu.Dropdown>
26
+ * </Menu>
27
+ * ```
28
+ */
29
+ export type MenuProps = RecursicaOverStyled<MuiMenuProps>;
30
+
31
+ const MenuBase = function Menu({ overStyled = false, ...rest }: MenuProps) {
32
+ const sanitizedProps = filterStylingProps(rest, overStyled);
33
+
34
+ // Bind CSS module classes to Mui's internal classNames API
35
+ const mergedClassNames: Partial<Record<string, string>> = {
36
+ dropdown: styles.dropdown,
37
+ item: styles.item,
38
+ itemLabel: styles.itemLabel,
39
+ itemSection: styles.itemSection,
40
+ divider: styles.divider,
41
+ label: styles.label,
42
+ chevron: styles.chevron,
43
+ };
44
+
45
+ const classNamesProp = (sanitizedProps as Record<string, unknown>).classNames;
46
+ if (
47
+ classNamesProp &&
48
+ typeof classNamesProp === "object" &&
49
+ !Array.isArray(classNamesProp)
50
+ ) {
51
+ const o = classNamesProp as Record<string, string>;
52
+ Object.keys(o).forEach((key) => {
53
+ if (mergedClassNames[key]) {
54
+ mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
55
+ } else {
56
+ mergedClassNames[key] = o[key];
57
+ }
58
+ });
59
+ }
60
+
61
+ return (
62
+ <MuiMenu
63
+ classes={mergedClassNames}
64
+ {...(sanitizedProps as unknown as MuiMenuProps)}
65
+ />
66
+ );
67
+ };
68
+ MenuBase.displayName = "Menu";
69
+
70
+ // ============================================================
71
+ // MENU TARGET
72
+ // ============================================================
73
+
74
+ /**
75
+ * Wrapper for the element that triggers the menu.
76
+ * Requires a single child element that supports ref forwarding.
77
+ */
78
+ export type MenuTargetProps = any;
79
+
80
+ const MenuTarget = function MenuTarget(props: MenuTargetProps) {
81
+ return <div {...props} />;
8
82
  };
83
+ MenuTarget.displayName = "MenuTarget";
84
+
85
+ // ============================================================
86
+ // MENU DROPDOWN
87
+ // ============================================================
88
+
89
+ /** The dropdown panel containing menu items, dividers, and labels. */
90
+ export type MenuDropdownProps = RecursicaOverStyled<any>;
91
+
92
+ const MenuDropdown = forwardRef<HTMLDivElement, MenuDropdownProps>(
93
+ function MenuDropdown({ overStyled = false, ...rest }, ref) {
94
+ const sanitizedProps = filterStylingProps(rest, overStyled);
95
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
96
+ .className as string | undefined;
97
+
98
+ return (
99
+ <div
100
+ ref={ref}
101
+ className={classNameProp}
102
+ {...(sanitizedProps as unknown as any)}
103
+ />
104
+ );
105
+ },
106
+ );
107
+ MenuDropdown.displayName = "MenuDropdown";
108
+
109
+ // ============================================================
110
+ // MENU ITEM
111
+ // ============================================================
112
+
113
+ /**
114
+ * An individual actionable item within the menu dropdown.
115
+ *
116
+ * **Note:** Mui's `color` prop is stripped in strict mode to enforce
117
+ * design token adherence. Use `overStyled={true}` if you need to bypass.
118
+ */
119
+ export type MenuItemProps = RecursicaOverStyled<Omit<any, "color">>;
120
+
121
+ const _MenuItem = forwardRef<HTMLButtonElement, MenuItemProps>(
122
+ function MenuItem({ overStyled = false, ...rest }, ref) {
123
+ const sanitizedProps = filterStylingProps(rest, overStyled);
124
+ const restRecord = sanitizedProps as Record<string, unknown>;
125
+
126
+ // Strip Mui's `color` prop to enforce token-driven styling
127
+ if (!overStyled) {
128
+ delete restRecord["color"];
129
+ }
130
+
131
+ const classNameProp = restRecord.className as string | undefined;
132
+
133
+ return (
134
+ <div
135
+ ref={ref}
136
+ className={classNameProp}
137
+ {...(sanitizedProps as unknown as any)}
138
+ />
139
+ );
140
+ },
141
+ );
142
+ _MenuItem.displayName = "MenuItem";
143
+
144
+ /**
145
+ * An individual actionable item within the menu dropdown.
146
+ *
147
+ * Supports polymorphism via the `component` prop for link-style items.
148
+ * @example
149
+ * ```tsx
150
+ * <Menu.Item component="a" href="/settings">Settings</Menu.Item>
151
+ * ```
152
+ */
153
+ const MenuItem = _MenuItem as any;
154
+
155
+ // ============================================================
156
+ // MENU DIVIDER
157
+ // ============================================================
158
+
159
+ /** A visual separator between groups of menu items. */
160
+ export type MenuDividerProps = RecursicaOverStyled<any>;
161
+
162
+ const MenuDivider = forwardRef<HTMLHRElement, MenuDividerProps>(
163
+ function MenuDivider({ overStyled = false, ...rest }, ref) {
164
+ const sanitizedProps = filterStylingProps(rest, overStyled);
165
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
166
+ .className as string | undefined;
167
+
168
+ return (
169
+ <hr
170
+ ref={ref}
171
+ className={classNameProp}
172
+ {...(sanitizedProps as unknown as any)}
173
+ />
174
+ );
175
+ },
176
+ );
177
+ MenuDivider.displayName = "MenuDivider";
178
+
179
+ // ============================================================
180
+ // MENU LABEL
181
+ // ============================================================
182
+
183
+ /** A non-interactive section label used to categorize groups of menu items. */
184
+ export type MenuLabelProps = RecursicaOverStyled<any>;
185
+
186
+ const MenuLabel = forwardRef<HTMLDivElement, MenuLabelProps>(function MenuLabel(
187
+ { overStyled = false, ...rest },
188
+ ref,
189
+ ) {
190
+ const sanitizedProps = filterStylingProps(rest, overStyled);
191
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
192
+ .className as string | undefined;
193
+
194
+ return (
195
+ <div
196
+ ref={ref}
197
+ className={classNameProp}
198
+ {...(sanitizedProps as unknown as any)}
199
+ />
200
+ );
201
+ });
202
+ MenuLabel.displayName = "MenuLabel";
203
+
204
+ // ============================================================
205
+ // MENU SUB (SUBMENU CONTAINER)
206
+ // ============================================================
207
+
208
+ /** Container for a submenu, wrapping Menu.Sub.Target and Menu.Sub.Dropdown. */
209
+ export type MenuSubProps = any;
210
+
211
+ const MenuSubBase = function MenuSub(props: MenuSubProps) {
212
+ return <div {...props} />;
213
+ };
214
+ MenuSubBase.displayName = "MenuSub";
215
+
216
+ // ============================================================
217
+ // MENU SUB TARGET
218
+ // ============================================================
219
+
220
+ /** Wrapper for the element that triggers the submenu. */
221
+ export type MenuSubTargetProps = any;
222
+
223
+ const MenuSubTarget = function MenuSubTarget(props: MenuSubTargetProps) {
224
+ return <div {...props} />;
225
+ };
226
+ MenuSubTarget.displayName = "MenuSubTarget";
227
+
228
+ // ============================================================
229
+ // MENU SUB ITEM (triggers submenu)
230
+ // ============================================================
231
+
232
+ /**
233
+ * A menu item that acts as a submenu trigger.
234
+ * Renders within Menu.Sub.Target and displays a chevron indicator.
235
+ */
236
+ export type MenuSubItemProps = RecursicaOverStyled<Omit<any, "color">>;
237
+
238
+ const _MenuSubItem = forwardRef<HTMLButtonElement, MenuSubItemProps>(
239
+ function MenuSubItem({ overStyled = false, ...rest }, ref) {
240
+ const sanitizedProps = filterStylingProps(rest, overStyled);
241
+ const restRecord = sanitizedProps as Record<string, unknown>;
242
+
243
+ if (!overStyled) {
244
+ delete restRecord["color"];
245
+ }
246
+
247
+ const classNameProp = restRecord.className as string | undefined;
248
+
249
+ return (
250
+ <div
251
+ ref={ref}
252
+ className={classNameProp}
253
+ {...(sanitizedProps as unknown as any)}
254
+ />
255
+ );
256
+ },
257
+ );
258
+ _MenuSubItem.displayName = "MenuSubItem";
259
+
260
+ /**
261
+ * A menu item that acts as a submenu trigger.
262
+ *
263
+ * Supports polymorphism via the `component` prop.
264
+ */
265
+ const MenuSubItem = _MenuSubItem as any;
266
+
267
+ // ============================================================
268
+ // MENU SUB DROPDOWN
269
+ // ============================================================
270
+
271
+ /** The dropdown panel for a submenu. */
272
+ export type MenuSubDropdownProps = RecursicaOverStyled<any>;
273
+
274
+ const MenuSubDropdown = forwardRef<HTMLDivElement, MenuSubDropdownProps>(
275
+ function MenuSubDropdown({ overStyled = false, ...rest }, ref) {
276
+ const sanitizedProps = filterStylingProps(rest, overStyled);
277
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
278
+ .className as string | undefined;
279
+
280
+ return (
281
+ <div
282
+ ref={ref}
283
+ className={classNameProp}
284
+ {...(sanitizedProps as unknown as any)}
285
+ />
286
+ );
287
+ },
288
+ );
289
+ MenuSubDropdown.displayName = "MenuSubDropdown";
290
+
291
+ // ============================================================
292
+ // DOT NOTATION EXPORT
293
+ // ============================================================
294
+
295
+ type MenuSubComponent = typeof MenuSubBase & {
296
+ Target: typeof MenuSubTarget;
297
+ Item: typeof MenuSubItem;
298
+ Dropdown: typeof MenuSubDropdown;
299
+ };
300
+
301
+ const MenuSub = MenuSubBase as MenuSubComponent;
302
+ MenuSub.Target = MenuSubTarget;
303
+ MenuSub.Item = MenuSubItem;
304
+ MenuSub.Dropdown = MenuSubDropdown;
305
+
306
+ type MenuComponent = typeof MenuBase & {
307
+ Target: typeof MenuTarget;
308
+ Dropdown: typeof MenuDropdown;
309
+ Item: typeof MenuItem;
310
+ Divider: typeof MenuDivider;
311
+ Label: typeof MenuLabel;
312
+ Sub: MenuSubComponent;
313
+ };
314
+
315
+ export const Menu = MenuBase as MenuComponent;
316
+ Menu.Target = MenuTarget;
317
+ Menu.Dropdown = MenuDropdown;
318
+ Menu.Item = MenuItem;
319
+ Menu.Divider = MenuDivider;
320
+ Menu.Label = MenuLabel;
321
+ Menu.Sub = MenuSub;
@@ -1,36 +1,172 @@
1
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_modal_properties_border-radius */
6
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_border-size */
7
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_button-gap */
8
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_colors_background */
9
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_colors_border-color */
10
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_colors_content */
11
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_colors_scroll-divider */
12
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_colors_title */
13
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_content-style_fontFamily */
14
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_content-style_fontSize */
15
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_content-style_fontStyle */
16
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_content-style_fontWeight */
17
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_content-style_letterSpacing */
18
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_content-style_lineHeight */
19
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_content-style_textCase */
20
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_content-style_textDecoration */
21
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_elevation */
22
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_header-style_fontFamily */
23
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_header-style_fontSize */
24
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_header-style_fontStyle */
25
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_header-style_fontWeight */
26
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_header-style_letterSpacing */
27
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_header-style_lineHeight */
28
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_header-style_textCase */
29
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_header-style_textDecoration */
30
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_horizontal-padding */
31
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_max-height */
32
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_max-width */
33
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_min-height */
34
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_min-width */
35
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_scroll-divider-thickness */
36
- /* recursica-ignore: --recursica_ui-kit_components_modal_properties_vertical-padding */
2
+ - content-style and header-style parent variables represent raw JSON styling objects rather than
3
+ direct CSS variables and cannot be applied directly in standard CSS files.
4
+ Sub-properties are fully and individually mapped to elements natively. */
5
+ /* recursica-ignore: --recursica_ui-kit_components_modal_properties_content-style */
6
+ /* recursica-ignore: --recursica_ui-kit_components_modal_properties_header-style */
7
+
8
+ .root {
9
+ }
10
+
11
+ .inner {
12
+ /* Mantine applies padding to inner to keep modal away from edges. We rely on content constraints. */
13
+ }
14
+
15
+ .content {
16
+ /* Geometric Bounds */
17
+ min-width: var(--recursica_ui-kit_components_modal_properties_min-width);
18
+ max-width: var(--recursica_ui-kit_components_modal_properties_max-width);
19
+ min-height: var(--recursica_ui-kit_components_modal_properties_min-height);
20
+
21
+ /* Restrict max-height to either the UI Kit max or the viewport bounds to force internal scrolling */
22
+ max-height: min(
23
+ var(--recursica_ui-kit_components_modal_properties_max-height),
24
+ calc(100vh - 4rem)
25
+ );
26
+ overflow: hidden;
27
+
28
+ /* Box model */
29
+ border-radius: var(
30
+ --recursica_ui-kit_components_modal_properties_border-radius
31
+ );
32
+ border-width: var(--recursica_ui-kit_components_modal_properties_border-size);
33
+ border-color: var(
34
+ --recursica_ui-kit_components_modal_properties_colors_border-color
35
+ );
36
+ border-style: solid;
37
+
38
+ /* Appearance */
39
+ background-color: var(
40
+ --recursica_ui-kit_components_modal_properties_colors_background
41
+ );
42
+ box-shadow: var(--recursica_ui-kit_components_modal_properties_elevation);
43
+
44
+ /* Internal Flex container to trap header and let body scroll */
45
+ display: flex;
46
+ flex-direction: column;
47
+ }
48
+
49
+ .header {
50
+ padding: var(--recursica_ui-kit_components_modal_properties_vertical-padding)
51
+ var(--recursica_ui-kit_components_modal_properties_horizontal-padding);
52
+ background-color: transparent; /* Inherit from content */
53
+ }
54
+
55
+ .title {
56
+ color: var(--recursica_ui-kit_components_modal_properties_colors_title);
57
+
58
+ /* Direct Figma Typography Mapping */
59
+ font-family: var(
60
+ --recursica_ui-kit_components_modal_properties_header-style_fontFamily
61
+ );
62
+ font-size: var(
63
+ --recursica_ui-kit_components_modal_properties_header-style_fontSize
64
+ );
65
+ font-style: var(
66
+ --recursica_ui-kit_components_modal_properties_header-style_fontStyle
67
+ );
68
+ font-weight: var(
69
+ --recursica_ui-kit_components_modal_properties_header-style_fontWeight
70
+ );
71
+ letter-spacing: var(
72
+ --recursica_ui-kit_components_modal_properties_header-style_letterSpacing
73
+ );
74
+ line-height: var(
75
+ --recursica_ui-kit_components_modal_properties_header-style_lineHeight
76
+ );
77
+ text-transform: var(
78
+ --recursica_ui-kit_components_modal_properties_header-style_textCase
79
+ );
80
+ text-decoration: var(
81
+ --recursica_ui-kit_components_modal_properties_header-style_textDecoration
82
+ );
83
+ }
84
+
85
+ .bodyWrapper {
86
+ /* This is the container that overrides Mantine's native Body. It strips padding and acts as a flex column. */
87
+ padding: 0;
88
+ display: flex;
89
+ flex-direction: column;
90
+ flex: 1 1 auto;
91
+ overflow: hidden;
92
+ min-height: 0; /* Critical for flex child scrolling constraint */
93
+ }
94
+
95
+ .scrollArea {
96
+ padding: var(--recursica_ui-kit_components_modal_properties_vertical-padding)
97
+ var(--recursica_ui-kit_components_modal_properties_horizontal-padding);
98
+
99
+ /* Internal scrolling */
100
+ flex: 1 1 auto;
101
+ overflow-y: auto;
102
+ min-height: 0; /* Critical for flex child scrolling constraint */
103
+
104
+ color: var(--recursica_ui-kit_components_modal_properties_colors_content);
105
+
106
+ /* Direct Figma Typography Mapping */
107
+ font-family: var(
108
+ --recursica_ui-kit_components_modal_properties_content-style_fontFamily
109
+ );
110
+ font-size: var(
111
+ --recursica_ui-kit_components_modal_properties_content-style_fontSize
112
+ );
113
+ font-style: var(
114
+ --recursica_ui-kit_components_modal_properties_content-style_fontStyle
115
+ );
116
+ font-weight: var(
117
+ --recursica_ui-kit_components_modal_properties_content-style_fontWeight
118
+ );
119
+ letter-spacing: var(
120
+ --recursica_ui-kit_components_modal_properties_content-style_letterSpacing
121
+ );
122
+ line-height: var(
123
+ --recursica_ui-kit_components_modal_properties_content-style_lineHeight
124
+ );
125
+ text-transform: var(
126
+ --recursica_ui-kit_components_modal_properties_content-style_textCase
127
+ );
128
+ text-decoration: var(
129
+ --recursica_ui-kit_components_modal_properties_content-style_textDecoration
130
+ );
131
+
132
+ /* Ensure button gap token is easily usable if developers use standard spacing */
133
+ --modal-button-gap: var(
134
+ --recursica_ui-kit_components_modal_properties_button-gap
135
+ );
136
+
137
+ /* Smooth divider transitions */
138
+ transition: border-color 0.15s ease;
139
+ border-top: var(
140
+ --recursica_ui-kit_components_modal_properties_scroll-divider-thickness
141
+ )
142
+ solid transparent;
143
+ border-bottom: var(
144
+ --recursica_ui-kit_components_modal_properties_scroll-divider-thickness
145
+ )
146
+ solid transparent;
147
+ }
148
+
149
+ .scrollArea[data-scrolled-top] {
150
+ border-top-color: var(
151
+ --recursica_ui-kit_components_modal_properties_colors_scroll-divider
152
+ );
153
+ }
154
+
155
+ .scrollArea[data-scrolled-bottom] {
156
+ border-bottom-color: var(
157
+ --recursica_ui-kit_components_modal_properties_colors_scroll-divider
158
+ );
159
+ }
160
+
161
+ .footer {
162
+ padding: var(--recursica_ui-kit_components_modal_properties_vertical-padding)
163
+ var(--recursica_ui-kit_components_modal_properties_horizontal-padding);
164
+ background-color: transparent;
165
+ display: flex;
166
+ justify-content: flex-end;
167
+ gap: var(--recursica_ui-kit_components_modal_properties_button-gap);
168
+ }
169
+
170
+ .close {
171
+ /* Inherits standard icon button tokens natively, but we can override if required */
172
+ }
@@ -1,35 +1,79 @@
1
+ import { useState } from "react";
1
2
  import type { Meta, StoryObj } from "@storybook/react";
2
- import { Modal } from "./Modal";
3
- import { ComingSoon } from "@recursica/storybook-template";
3
+ import { Modal, type ModalProps } from "./Modal";
4
+ import { Button } from "../Button";
4
5
 
5
6
  const meta: Meta<typeof Modal> = {
6
- title: "UI-Kit/🚧 Modal",
7
+ title: "UI-Kit/Modal",
7
8
  component: Modal,
8
9
  tags: ["autodocs"],
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
- ],
26
- },
10
+ argTypes: {
11
+ opened: { table: { disable: true } },
12
+ onClose: { table: { disable: true } },
13
+ defaultChecked: { table: { disable: true } },
27
14
  },
28
15
  };
29
16
 
30
17
  export default meta;
18
+
31
19
  type Story = StoryObj<typeof Modal>;
32
20
 
21
+ const DefaultWrapper = (args: ModalProps) => {
22
+ const [opened, setOpened] = useState(false);
23
+ return (
24
+ <>
25
+ <Modal {...args} opened={opened} onClose={() => setOpened(false)}>
26
+ Please log in to continue accessing this feature.
27
+ <Modal.Footer>
28
+ <Button variant="outline" onClick={() => setOpened(false)}>
29
+ Cancel
30
+ </Button>
31
+ <Button onClick={() => setOpened(false)}>Confirm</Button>
32
+ </Modal.Footer>
33
+ </Modal>
34
+ <Button onClick={() => setOpened(true)}>Open Modal</Button>
35
+ </>
36
+ );
37
+ };
38
+
33
39
  export const Default: Story = {
34
- render: () => <ComingSoon componentName="Modal" />,
40
+ args: {
41
+ title: "Authentication Required",
42
+ },
43
+ render: (args) => <DefaultWrapper {...args} />,
44
+ };
45
+
46
+ const ScrollingWrapper = (args: ModalProps) => {
47
+ const [opened, setOpened] = useState(false);
48
+ return (
49
+ <>
50
+ <Modal {...args} opened={opened} onClose={() => setOpened(false)}>
51
+ <p>
52
+ This modal demonstrates the dynamically injected scroll dividers. When
53
+ this body content overflows, the borders between the Header and Footer
54
+ automatically appear to define the scrolling boundary.
55
+ </p>
56
+ {Array(20)
57
+ .fill(0)
58
+ .map((_, i) => (
59
+ <p key={i}>Scrolling content block {i + 1}...</p>
60
+ ))}
61
+ {/* The new Footer abstraction */}
62
+ <Modal.Footer>
63
+ <Button variant="outline" onClick={() => setOpened(false)}>
64
+ Decline
65
+ </Button>
66
+ <Button onClick={() => setOpened(false)}>Accept Terms</Button>
67
+ </Modal.Footer>
68
+ </Modal>
69
+ <Button onClick={() => setOpened(true)}>Open Scrolling Modal</Button>
70
+ </>
71
+ );
72
+ };
73
+
74
+ export const ScrollingContent: Story = {
75
+ args: {
76
+ title: "Terms and Conditions",
77
+ },
78
+ render: (args) => <ScrollingWrapper {...args} />,
35
79
  };