@recursica/mui-adapter 0.27.0 → 0.28.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.
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/mui-adapter.cjs +55 -55
- package/dist/mui-adapter.cjs.map +1 -1
- package/dist/mui-adapter.css +1 -1
- package/dist/mui-adapter.js +3502 -3443
- package/dist/mui-adapter.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Breadcrumb/Breadcrumb.module.css +10 -5
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -6
- package/src/components/Breadcrumb/Breadcrumb.tsx +1 -0
- package/src/components/Dropdown/Dropdown.icons.tsx +33 -0
- package/src/components/Dropdown/Dropdown.module.css +117 -15
- package/src/components/Dropdown/Dropdown.stories.tsx +21 -0
- package/src/components/Dropdown/Dropdown.tsx +90 -12
- package/src/components/Dropdown/USAGE.md +7 -0
- package/src/components/HoverCard/HoverCard.module.css +25 -3
- package/src/components/Menu/Menu.module.css +3 -0
- package/src/components/Menu/Menu.stories.tsx +128 -5
- package/src/components/Menu/Menu.tsx +1 -1
|
@@ -12,6 +12,7 @@ import type { Meta, StoryObj } from "@storybook/react";
|
|
|
12
12
|
import { Menu, MenuItem, MenuDivider } from "./Menu";
|
|
13
13
|
import { Button } from "../Button";
|
|
14
14
|
import { ListSubheader } from "@mui/material";
|
|
15
|
+
import styles from "./Menu.module.css";
|
|
15
16
|
|
|
16
17
|
const SettingsIcon = (props: React.ComponentProps<"svg">) => (
|
|
17
18
|
<svg
|
|
@@ -123,6 +124,56 @@ const ArrowsIcon = (props: React.ComponentProps<"svg">) => (
|
|
|
123
124
|
</svg>
|
|
124
125
|
);
|
|
125
126
|
|
|
127
|
+
const ChevronRightIcon = (props: React.ComponentProps<"svg">) => (
|
|
128
|
+
<svg
|
|
129
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
130
|
+
width="14"
|
|
131
|
+
height="14"
|
|
132
|
+
viewBox="0 0 24 24"
|
|
133
|
+
fill="none"
|
|
134
|
+
stroke="currentColor"
|
|
135
|
+
strokeWidth="2"
|
|
136
|
+
strokeLinecap="round"
|
|
137
|
+
strokeLinejoin="round"
|
|
138
|
+
{...props}
|
|
139
|
+
>
|
|
140
|
+
<polyline points="9 18 15 12 9 6" />
|
|
141
|
+
</svg>
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// mui-adapter's Menu is monolithic (see IMPLEMENTATION_NOTES.md) — there is no
|
|
145
|
+
// Menu.Sub composable, so a submenu is just a MenuItem that anchors its own
|
|
146
|
+
// nested Menu, mirroring how the top-level trigger manages its own anchorEl.
|
|
147
|
+
interface SubMenuTriggerProps {
|
|
148
|
+
label: string;
|
|
149
|
+
children: React.ReactNode;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const SubMenuTrigger = ({ label, children }: SubMenuTriggerProps) => {
|
|
153
|
+
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<>
|
|
157
|
+
<MenuItem onClick={(event) => setAnchorEl(event.currentTarget)}>
|
|
158
|
+
{label}
|
|
159
|
+
<ChevronRightIcon
|
|
160
|
+
className={styles.chevron}
|
|
161
|
+
style={{ marginLeft: "auto", paddingLeft: 8 }}
|
|
162
|
+
/>
|
|
163
|
+
</MenuItem>
|
|
164
|
+
<Menu
|
|
165
|
+
anchorEl={anchorEl}
|
|
166
|
+
open={Boolean(anchorEl)}
|
|
167
|
+
onClose={() => setAnchorEl(null)}
|
|
168
|
+
anchorOrigin={{ vertical: "top", horizontal: "right" }}
|
|
169
|
+
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
|
170
|
+
>
|
|
171
|
+
{children}
|
|
172
|
+
</Menu>
|
|
173
|
+
</>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
176
|
+
|
|
126
177
|
type MenuStoryArgs = Record<string, unknown>;
|
|
127
178
|
|
|
128
179
|
const meta: Meta = {
|
|
@@ -225,9 +276,84 @@ export const WithDisabledItems: Story = {
|
|
|
225
276
|
},
|
|
226
277
|
};
|
|
227
278
|
|
|
228
|
-
export const
|
|
279
|
+
export const WithSubmenus: Story = {
|
|
229
280
|
render: (args) => (
|
|
230
281
|
<InteractiveMenu {...args}>
|
|
282
|
+
<MenuItem>Dashboard</MenuItem>
|
|
283
|
+
<SubMenuTrigger label="Products">
|
|
284
|
+
<MenuItem>All products</MenuItem>
|
|
285
|
+
<MenuItem>Categories</MenuItem>
|
|
286
|
+
<MenuItem>Tags</MenuItem>
|
|
287
|
+
</SubMenuTrigger>
|
|
288
|
+
<SubMenuTrigger label="Orders">
|
|
289
|
+
<MenuItem>Open</MenuItem>
|
|
290
|
+
<MenuItem>Completed</MenuItem>
|
|
291
|
+
<MenuItem>Cancelled</MenuItem>
|
|
292
|
+
</SubMenuTrigger>
|
|
293
|
+
</InteractiveMenu>
|
|
294
|
+
),
|
|
295
|
+
args: {
|
|
296
|
+
opened: true,
|
|
297
|
+
// Match mantine's explicit `width: 200` for this story so the row has room for the
|
|
298
|
+
// "Products"/"Orders" label plus the submenu chevron without clipping it.
|
|
299
|
+
slotProps: { paper: { style: { minWidth: 200 } } },
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// mui-adapter's Menu has no native hover-trigger support (unlike Mantine's `trigger` prop),
|
|
304
|
+
// so this story implements open-on-hover itself: hovering the target opens the menu, and a
|
|
305
|
+
// short close delay (mirroring Mantine's `closeDelay`) keeps it open while the pointer moves
|
|
306
|
+
// from the target into the dropdown, since MUI's Menu renders in a portal outside the
|
|
307
|
+
// wrapper's DOM subtree.
|
|
308
|
+
const HoverMenu = ({ children, ...args }: InteractiveMenuProps) => {
|
|
309
|
+
const [open, setOpen] = useState(false);
|
|
310
|
+
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
311
|
+
const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(
|
|
312
|
+
undefined,
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
const cancelClose = () => {
|
|
316
|
+
if (closeTimer.current) clearTimeout(closeTimer.current);
|
|
317
|
+
};
|
|
318
|
+
const scheduleClose = () => {
|
|
319
|
+
closeTimer.current = setTimeout(() => setOpen(false), 300);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
return (
|
|
323
|
+
<div>
|
|
324
|
+
<Button
|
|
325
|
+
ref={buttonRef}
|
|
326
|
+
variant="solid"
|
|
327
|
+
onClick={() => setOpen((prev) => !prev)}
|
|
328
|
+
onMouseEnter={() => {
|
|
329
|
+
cancelClose();
|
|
330
|
+
setOpen(true);
|
|
331
|
+
}}
|
|
332
|
+
onMouseLeave={scheduleClose}
|
|
333
|
+
>
|
|
334
|
+
Hover or Click
|
|
335
|
+
</Button>
|
|
336
|
+
<Menu
|
|
337
|
+
{...args}
|
|
338
|
+
anchorEl={buttonRef.current}
|
|
339
|
+
open={open}
|
|
340
|
+
onClose={() => setOpen(false)}
|
|
341
|
+
slotProps={{
|
|
342
|
+
paper: {
|
|
343
|
+
onMouseEnter: cancelClose,
|
|
344
|
+
onMouseLeave: scheduleClose,
|
|
345
|
+
},
|
|
346
|
+
}}
|
|
347
|
+
>
|
|
348
|
+
{children}
|
|
349
|
+
</Menu>
|
|
350
|
+
</div>
|
|
351
|
+
);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
export const HoverTrigger: Story = {
|
|
355
|
+
render: (args) => (
|
|
356
|
+
<HoverMenu {...args}>
|
|
231
357
|
<MenuItem>
|
|
232
358
|
<SettingsIcon style={{ marginRight: 8 }} /> Settings
|
|
233
359
|
</MenuItem>
|
|
@@ -237,9 +363,6 @@ export const HoverTrigger: Story = {
|
|
|
237
363
|
<MenuItem>
|
|
238
364
|
<ImageIcon style={{ marginRight: 8 }} /> Gallery
|
|
239
365
|
</MenuItem>
|
|
240
|
-
</
|
|
366
|
+
</HoverMenu>
|
|
241
367
|
),
|
|
242
|
-
args: {
|
|
243
|
-
opened: true,
|
|
244
|
-
},
|
|
245
368
|
};
|
|
@@ -38,7 +38,7 @@ export const Menu = forwardRef<HTMLDivElement, MenuProps>(function Menu(
|
|
|
38
38
|
<MuiMenu
|
|
39
39
|
ref={ref}
|
|
40
40
|
{...(sanitizedProps as MuiMenuProps)}
|
|
41
|
-
className={
|
|
41
|
+
className={className}
|
|
42
42
|
classes={mergedClassNames}
|
|
43
43
|
/>
|
|
44
44
|
);
|