@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,35 +1,239 @@
1
+ /**
2
+ * Menu story axes:
3
+ * - trigger: click (default) vs hover behavior
4
+ * - position: dropdown alignment relative to target
5
+ * - withArrow: arrow indicator on the dropdown
6
+ * - content variations: icons, dividers, labels, disabled items, submenus
7
+ * - layer: tested via the global Layer decorator (withLayer/layer args)
8
+ */
9
+
10
+ import React, { useState, useRef, useEffect } from "react";
1
11
  import type { Meta, StoryObj } from "@storybook/react";
2
- import { Menu } from "./Menu";
3
- import { ComingSoon } from "@recursica/storybook-template";
12
+ import { Menu, MenuItem, MenuDivider } from "./Menu";
13
+ import { Button } from "../Button";
14
+ import { ListSubheader } from "@mui/material";
15
+
16
+ const SettingsIcon = (props: any) => (
17
+ <svg
18
+ xmlns="http://www.w3.org/2000/svg"
19
+ width="14"
20
+ height="14"
21
+ viewBox="0 0 24 24"
22
+ fill="none"
23
+ stroke="currentColor"
24
+ strokeWidth="2"
25
+ strokeLinecap="round"
26
+ strokeLinejoin="round"
27
+ {...props}
28
+ >
29
+ <circle cx="12" cy="12" r="3" />
30
+ <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
31
+ </svg>
32
+ );
33
+
34
+ const SearchIcon = (props: any) => (
35
+ <svg
36
+ xmlns="http://www.w3.org/2000/svg"
37
+ width="14"
38
+ height="14"
39
+ viewBox="0 0 24 24"
40
+ fill="none"
41
+ stroke="currentColor"
42
+ strokeWidth="2"
43
+ strokeLinecap="round"
44
+ strokeLinejoin="round"
45
+ {...props}
46
+ >
47
+ <circle cx="11" cy="11" r="8" />
48
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
49
+ </svg>
50
+ );
51
+
52
+ const MessageIcon = (props: any) => (
53
+ <svg
54
+ xmlns="http://www.w3.org/2000/svg"
55
+ width="14"
56
+ height="14"
57
+ viewBox="0 0 24 24"
58
+ fill="none"
59
+ stroke="currentColor"
60
+ strokeWidth="2"
61
+ strokeLinecap="round"
62
+ strokeLinejoin="round"
63
+ {...props}
64
+ >
65
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
66
+ </svg>
67
+ );
68
+
69
+ const ImageIcon = (props: any) => (
70
+ <svg
71
+ xmlns="http://www.w3.org/2000/svg"
72
+ width="14"
73
+ height="14"
74
+ viewBox="0 0 24 24"
75
+ fill="none"
76
+ stroke="currentColor"
77
+ strokeWidth="2"
78
+ strokeLinecap="round"
79
+ strokeLinejoin="round"
80
+ {...props}
81
+ >
82
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
83
+ <circle cx="8.5" cy="8.5" r="1.5" />
84
+ <polyline points="21 15 16 10 5 21" />
85
+ </svg>
86
+ );
87
+
88
+ const TrashIcon = (props: any) => (
89
+ <svg
90
+ xmlns="http://www.w3.org/2000/svg"
91
+ width="14"
92
+ height="14"
93
+ viewBox="0 0 24 24"
94
+ fill="none"
95
+ stroke="currentColor"
96
+ strokeWidth="2"
97
+ strokeLinecap="round"
98
+ strokeLinejoin="round"
99
+ {...props}
100
+ >
101
+ <polyline points="3 6 5 6 21 6" />
102
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
103
+ </svg>
104
+ );
4
105
 
5
- const meta: Meta<typeof Menu> = {
6
- title: "UI-Kit/🚧 Menu",
106
+ const ArrowsIcon = (props: any) => (
107
+ <svg
108
+ xmlns="http://www.w3.org/2000/svg"
109
+ width="14"
110
+ height="14"
111
+ viewBox="0 0 24 24"
112
+ fill="none"
113
+ stroke="currentColor"
114
+ strokeWidth="2"
115
+ strokeLinecap="round"
116
+ strokeLinejoin="round"
117
+ {...props}
118
+ >
119
+ <polyline points="17 1 21 5 17 9" />
120
+ <path d="M3 11V9a4 4 0 0 1 4-4h14" />
121
+ <polyline points="7 23 3 19 7 15" />
122
+ <path d="M21 13v2a4 4 0 0 1-4 4H3" />
123
+ </svg>
124
+ );
125
+
126
+ type MenuStoryArgs = Record<string, any>;
127
+
128
+ const meta: Meta = {
129
+ title: "UI-Kit/Menu",
7
130
  component: Menu,
8
131
  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
- },
27
- },
28
132
  };
29
133
 
30
134
  export default meta;
31
- type Story = StoryObj<typeof Menu>;
135
+
136
+ type Story = StoryObj<MenuStoryArgs>;
137
+
138
+ const InteractiveMenu = ({ children, openDelay, ...args }: any) => {
139
+ const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
140
+ const buttonRef = useRef<HTMLButtonElement>(null);
141
+
142
+ const open = Boolean(anchorEl) || args.opened;
143
+
144
+ useEffect(() => {
145
+ if (args.opened && buttonRef.current) {
146
+ setAnchorEl(buttonRef.current);
147
+ }
148
+ }, [args.opened]);
149
+
150
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
151
+ setAnchorEl(event.currentTarget);
152
+ };
153
+ const handleClose = () => {
154
+ setAnchorEl(null);
155
+ };
156
+
157
+ return (
158
+ <div>
159
+ <Button ref={buttonRef} variant="solid" onClick={handleClick}>
160
+ Toggle Menu
161
+ </Button>
162
+ <Menu anchorEl={anchorEl} open={open} onClose={handleClose} {...args}>
163
+ {children}
164
+ </Menu>
165
+ </div>
166
+ );
167
+ };
32
168
 
33
169
  export const Default: Story = {
34
- render: () => <ComingSoon componentName="Menu" />,
170
+ render: (args) => (
171
+ <InteractiveMenu {...args}>
172
+ <ListSubheader>Application</ListSubheader>
173
+ <MenuItem>
174
+ <SettingsIcon style={{ marginRight: 8 }} /> Settings
175
+ </MenuItem>
176
+ <MenuItem>
177
+ <MessageIcon style={{ marginRight: 8 }} /> Messages
178
+ </MenuItem>
179
+ <MenuItem>
180
+ <ImageIcon style={{ marginRight: 8 }} /> Gallery
181
+ </MenuItem>
182
+ <MenuItem>
183
+ <SearchIcon style={{ marginRight: 8 }} /> Search
184
+ </MenuItem>
185
+ <MenuDivider />
186
+ <ListSubheader>Danger zone</ListSubheader>
187
+ <MenuItem>
188
+ <ArrowsIcon style={{ marginRight: 8 }} /> Transfer my data
189
+ </MenuItem>
190
+ <MenuItem>
191
+ <TrashIcon style={{ marginRight: 8 }} /> Delete my account
192
+ </MenuItem>
193
+ </InteractiveMenu>
194
+ ),
195
+ args: {
196
+ opened: true,
197
+ },
198
+ };
199
+
200
+ export const WithDisabledItems: Story = {
201
+ render: (args) => (
202
+ <InteractiveMenu {...args}>
203
+ <MenuItem>
204
+ <SettingsIcon style={{ marginRight: 8 }} /> Settings
205
+ </MenuItem>
206
+ <MenuItem disabled>
207
+ <SearchIcon style={{ marginRight: 8 }} /> Search (disabled)
208
+ </MenuItem>
209
+ <MenuItem>
210
+ <MessageIcon style={{ marginRight: 8 }} /> Messages
211
+ </MenuItem>
212
+ <MenuItem disabled>
213
+ <TrashIcon style={{ marginRight: 8 }} /> Delete (disabled)
214
+ </MenuItem>
215
+ </InteractiveMenu>
216
+ ),
217
+ args: {
218
+ opened: true,
219
+ },
220
+ };
221
+
222
+ export const HoverTrigger: Story = {
223
+ render: (args) => (
224
+ <InteractiveMenu {...args}>
225
+ <MenuItem>
226
+ <SettingsIcon style={{ marginRight: 8 }} /> Settings
227
+ </MenuItem>
228
+ <MenuItem>
229
+ <MessageIcon style={{ marginRight: 8 }} /> Messages
230
+ </MenuItem>
231
+ <MenuItem>
232
+ <ImageIcon style={{ marginRight: 8 }} /> Gallery
233
+ </MenuItem>
234
+ </InteractiveMenu>
235
+ ),
236
+ args: {
237
+ opened: true,
238
+ },
35
239
  };
@@ -1,8 +1,75 @@
1
- import React from "react";
2
- import "./Menu.module.css";
1
+ import { forwardRef } from "react";
2
+ import {
3
+ Menu as MuiMenu,
4
+ type MenuProps as MuiMenuProps,
5
+ MenuItem as MuiMenuItem,
6
+ type MenuItemProps as MuiMenuItemProps,
7
+ Divider as MuiDivider,
8
+ type DividerProps as MuiDividerProps,
9
+ } from "@mui/material";
10
+ import {
11
+ filterStylingProps,
12
+ type RecursicaOverStyled,
13
+ } from "../../utils/filterStylingProps";
14
+ import styles from "./Menu.module.css";
3
15
 
4
- export type MenuProps = React.HTMLAttributes<HTMLDivElement>;
16
+ import { type RecursicaMenuProps } from "@recursica/adapter-common";
5
17
 
6
- export const Menu: React.FC<MenuProps> = (props) => {
7
- return <div {...props}>Menu</div>;
8
- };
18
+ export type MenuProps = RecursicaOverStyled<MuiMenuProps & RecursicaMenuProps>;
19
+
20
+ export const Menu = forwardRef<HTMLDivElement, MenuProps>(function Menu(
21
+ { overStyled = false, className, ...rest },
22
+ ref,
23
+ ) {
24
+ const sanitizedProps = filterStylingProps(rest, overStyled);
25
+
26
+ return (
27
+ <MuiMenu
28
+ ref={ref}
29
+ className={`${styles.dropdown} ${className || ""}`}
30
+ classes={{
31
+ paper: styles.dropdown,
32
+ list: styles.dropdown,
33
+ }}
34
+ {...(sanitizedProps as MuiMenuProps)}
35
+ />
36
+ );
37
+ });
38
+
39
+ Menu.displayName = "Menu";
40
+
41
+ export type MenuItemProps = RecursicaOverStyled<MuiMenuItemProps>;
42
+
43
+ export const MenuItem = forwardRef<HTMLLIElement, MenuItemProps>(
44
+ function MenuItem({ overStyled = false, className, ...rest }, ref) {
45
+ const sanitizedProps = filterStylingProps(rest, overStyled);
46
+
47
+ return (
48
+ <MuiMenuItem
49
+ ref={ref}
50
+ className={`${styles.item} ${className || ""}`}
51
+ {...(sanitizedProps as MuiMenuItemProps)}
52
+ />
53
+ );
54
+ },
55
+ );
56
+
57
+ MenuItem.displayName = "MenuItem";
58
+
59
+ export type MenuDividerProps = RecursicaOverStyled<MuiDividerProps>;
60
+
61
+ export const MenuDivider = forwardRef<HTMLHRElement, MenuDividerProps>(
62
+ function MenuDivider({ overStyled = false, className, ...rest }, ref) {
63
+ const sanitizedProps = filterStylingProps(rest, overStyled);
64
+
65
+ return (
66
+ <MuiDivider
67
+ ref={ref}
68
+ className={`${styles.divider} ${className || ""}`}
69
+ {...(sanitizedProps as MuiDividerProps)}
70
+ />
71
+ );
72
+ },
73
+ );
74
+
75
+ MenuDivider.displayName = "MenuDivider";
@@ -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
  };