better-mui-menu 1.0.8 → 1.1.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/dist/index.cjs +43 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -6
- package/dist/index.d.ts +5 -6
- package/dist/index.js +43 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -67,16 +67,13 @@ var transitionConfig = {
|
|
|
67
67
|
type: import_Fade.default,
|
|
68
68
|
timeout: { enter: CLOSE_DELAY + 50, exit: CLOSE_DELAY + 50 }
|
|
69
69
|
};
|
|
70
|
-
var
|
|
70
|
+
var DEFAULT_ELEVATION = 8;
|
|
71
|
+
var MenuItemContent = (0, import_material.styled)(import_material.Stack)({
|
|
71
72
|
flexDirection: "row",
|
|
72
73
|
alignItems: "center",
|
|
73
|
-
gap:
|
|
74
|
-
fontSize: theme.typography.body1.fontSize,
|
|
75
|
-
"& .MuiSvgIcon-root": {
|
|
76
|
-
fontSize: theme.typography.body2.fontSize
|
|
77
|
-
},
|
|
74
|
+
gap: "8px",
|
|
78
75
|
padding: 0
|
|
79
|
-
})
|
|
76
|
+
});
|
|
80
77
|
|
|
81
78
|
// src/Menu/NestedMenuItem.tsx
|
|
82
79
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
@@ -90,6 +87,7 @@ var NestedMenuItem = (props) => {
|
|
|
90
87
|
children,
|
|
91
88
|
items,
|
|
92
89
|
endIcon: _,
|
|
90
|
+
menuProps,
|
|
93
91
|
...menuItemProps
|
|
94
92
|
} = props;
|
|
95
93
|
const [subMenuAnchorEl, setSubMenuAnchorEl] = (0, import_react.useState)(null);
|
|
@@ -127,7 +125,7 @@ var NestedMenuItem = (props) => {
|
|
|
127
125
|
const clonedOnClick = (event) => {
|
|
128
126
|
childOnClick?.(event);
|
|
129
127
|
handleClose();
|
|
130
|
-
parentMenuClose();
|
|
128
|
+
parentMenuClose?.(event, "itemClick", menuItemId);
|
|
131
129
|
};
|
|
132
130
|
return (0, import_react.cloneElement)(child, { onClick: clonedOnClick });
|
|
133
131
|
}
|
|
@@ -160,7 +158,8 @@ var NestedMenuItem = (props) => {
|
|
|
160
158
|
startIcon: NestedMenuItemStartIcon,
|
|
161
159
|
endIcon: NestedMenuItemEndIcon,
|
|
162
160
|
parentMenuClose,
|
|
163
|
-
items: entryItems
|
|
161
|
+
items: entryItems,
|
|
162
|
+
menuProps
|
|
164
163
|
},
|
|
165
164
|
entryKey
|
|
166
165
|
);
|
|
@@ -168,7 +167,7 @@ var NestedMenuItem = (props) => {
|
|
|
168
167
|
const handleItemClick = (event) => {
|
|
169
168
|
onClick?.(event);
|
|
170
169
|
handleClose();
|
|
171
|
-
parentMenuClose();
|
|
170
|
+
parentMenuClose?.(event, "itemClick", entryId);
|
|
172
171
|
};
|
|
173
172
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_MenuItem.default, { onClick: handleItemClick, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(MenuItemContent, { children: [
|
|
174
173
|
NestedMenuItemStartIcon ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(NestedMenuItemStartIcon, {}) : null,
|
|
@@ -236,43 +235,34 @@ var NestedMenuItem = (props) => {
|
|
|
236
235
|
if (isNodeInstance(e.relatedTarget) && menuItemRef.current?.contains(e.relatedTarget)) return;
|
|
237
236
|
scheduleClose();
|
|
238
237
|
},
|
|
239
|
-
children: ({ TransitionProps }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Fade2.default, { ...TransitionProps, timeout: transitionConfig.timeout, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
240
|
-
import_material2.
|
|
238
|
+
children: ({ TransitionProps }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_Fade2.default, { ...TransitionProps, timeout: transitionConfig.timeout, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_material2.Paper, { elevation: menuProps.elevation, ...menuProps?.slotProps?.paper || {}, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
239
|
+
import_material2.MenuList,
|
|
241
240
|
{
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
nativeEvent.__nestedMenuArrowLeftHandled = true;
|
|
264
|
-
e.preventDefault();
|
|
265
|
-
e.stopPropagation();
|
|
266
|
-
nativeEvent.stopImmediatePropagation();
|
|
267
|
-
handleClose();
|
|
268
|
-
menuItemRef.current?.focus();
|
|
269
|
-
}
|
|
270
|
-
},
|
|
271
|
-
children: renderedSubMenuItems
|
|
241
|
+
id: subMenuId,
|
|
242
|
+
"aria-labelledby": menuItemId,
|
|
243
|
+
role: "menu",
|
|
244
|
+
...menuProps?.slotProps?.list || {},
|
|
245
|
+
autoFocusItem: true,
|
|
246
|
+
onKeyDown: (e) => {
|
|
247
|
+
if (e.key === "ArrowLeft") {
|
|
248
|
+
const { nativeEvent } = e;
|
|
249
|
+
if (nativeEvent.__nestedMenuArrowLeftHandled) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (!subMenuRef.current?.contains(e.target)) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
nativeEvent.__nestedMenuArrowLeftHandled = true;
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
e.stopPropagation();
|
|
258
|
+
nativeEvent.stopImmediatePropagation();
|
|
259
|
+
handleClose();
|
|
260
|
+
menuItemRef.current?.focus();
|
|
272
261
|
}
|
|
273
|
-
|
|
262
|
+
},
|
|
263
|
+
children: renderedSubMenuItems
|
|
274
264
|
}
|
|
275
|
-
) })
|
|
265
|
+
) }) })
|
|
276
266
|
}
|
|
277
267
|
)
|
|
278
268
|
] });
|
|
@@ -280,7 +270,8 @@ var NestedMenuItem = (props) => {
|
|
|
280
270
|
|
|
281
271
|
// src/Menu/index.tsx
|
|
282
272
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
283
|
-
function Menu({
|
|
273
|
+
function Menu({ items, elevation = DEFAULT_ELEVATION, ...rest }) {
|
|
274
|
+
const menuProps = { elevation, ...rest };
|
|
284
275
|
const generatedMenuId = (0, import_react2.useId)();
|
|
285
276
|
const renderedMenuEntries = items.map((item, index) => {
|
|
286
277
|
if (item.type === "divider") {
|
|
@@ -307,15 +298,16 @@ function Menu({ anchorEl, open, onClose, items }) {
|
|
|
307
298
|
label: displayLabel,
|
|
308
299
|
startIcon: StartIconComponent,
|
|
309
300
|
endIcon: EndIconComponent,
|
|
310
|
-
parentMenuClose: onClose,
|
|
311
|
-
items: nestedItems
|
|
301
|
+
parentMenuClose: menuProps.onClose,
|
|
302
|
+
items: nestedItems,
|
|
303
|
+
menuProps
|
|
312
304
|
},
|
|
313
305
|
itemKey
|
|
314
306
|
);
|
|
315
307
|
}
|
|
316
308
|
const handleItemClick = (event) => {
|
|
317
309
|
onClick?.(event);
|
|
318
|
-
onClose();
|
|
310
|
+
menuProps.onClose?.(event, "itemClick", entryId);
|
|
319
311
|
};
|
|
320
312
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_MenuItem2.default, { onClick: handleItemClick, ...menuItemProps, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MenuItemContent, { children: [
|
|
321
313
|
StartIconComponent ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StartIconComponent, {}) : null,
|
|
@@ -327,19 +319,9 @@ function Menu({ anchorEl, open, onClose, items }) {
|
|
|
327
319
|
import_Menu.default,
|
|
328
320
|
{
|
|
329
321
|
"data-testid": "root-menu",
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
onKeyDown: (e) => {
|
|
334
|
-
if (e.key === "Escape" && open) onClose();
|
|
335
|
-
},
|
|
336
|
-
transitionDuration: transitionConfig.timeout,
|
|
337
|
-
slots: { transition: transitionConfig.type },
|
|
338
|
-
slotProps: {
|
|
339
|
-
list: {
|
|
340
|
-
"aria-labelledby": "icon-menu-button"
|
|
341
|
-
}
|
|
342
|
-
},
|
|
322
|
+
...menuProps,
|
|
323
|
+
transitionDuration: menuProps.transitionDuration || transitionConfig.timeout,
|
|
324
|
+
slots: { transition: transitionConfig.type, ...menuProps.slots },
|
|
343
325
|
children: renderedMenuEntries
|
|
344
326
|
}
|
|
345
327
|
);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/Menu/index.tsx","../src/Menu/NestedMenuItem.tsx","../../../node_modules/@mui/icons-material/esm/utils/createSvgIcon.js","../../../node_modules/@mui/icons-material/esm/ArrowRight.js","../src/Menu/common.ts"],"sourcesContent":["import { Menu } from './Menu'\n\nexport type { MenuItem } from './Menu/types'\nexport default Menu\n","import MuiMenu from '@mui/material/Menu';\nimport Divider from '@mui/material/Divider';\nimport MuiMenuItem from '@mui/material/MenuItem';\nimport React, { useId } from 'react';\nimport { Typography } from '@mui/material';\nimport { NestedMenuItem } from './NestedMenuItem';\nimport type { MenuItem } from './types';\nimport { MenuItemContent, transitionConfig } from './common';\n\ntype Props = {\n anchorEl: null | HTMLElement;\n open: boolean;\n onClose: () => void;\n items: MenuItem[];\n};\n\nexport function Menu({ anchorEl, open, onClose, items }: Props) {\n const generatedMenuId = useId();\n\n const renderedMenuEntries = items.map((item, index) => {\n if (item.type === 'divider') {\n return <Divider key={`divider-${index}`} />;\n }\n\n const {\n type: _,\n items: nestedItems,\n startIcon: StartIconComponent,\n endIcon: EndIconComponent,\n label,\n onClick,\n id,\n ...menuItemProps\n } = item;\n const entryId = id ?? `${generatedMenuId}-entry-${index}`;\n const itemKey = `menu-item-id:${entryId}`;\n const displayLabel = label ?? entryId;\n\n if (nestedItems && nestedItems.length > 0) {\n return (\n <NestedMenuItem\n key={itemKey}\n id={entryId}\n label={displayLabel}\n startIcon={StartIconComponent}\n endIcon={EndIconComponent}\n parentMenuClose={onClose}\n items={nestedItems}\n />\n );\n }\n\n const handleItemClick = (event: React.MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n onClose();\n };\n\n return (\n <MuiMenuItem key={itemKey} onClick={handleItemClick} {...menuItemProps}>\n <MenuItemContent>\n {StartIconComponent ? <StartIconComponent /> : null}\n <Typography sx={{ flex: 1 }}>{displayLabel}</Typography>\n {EndIconComponent ? <EndIconComponent /> : null}\n </MenuItemContent>\n </MuiMenuItem>\n );\n });\n\n return (\n <MuiMenu\n data-testid='root-menu'\n anchorEl={anchorEl}\n open={open}\n onClose={onClose}\n onKeyDown={e => {\n if (e.key === 'Escape' && open) onClose();\n }}\n transitionDuration={transitionConfig.timeout}\n slots={{ transition: transitionConfig.type }}\n slotProps={{\n list: {\n 'aria-labelledby': 'icon-menu-button',\n },\n }}\n >\n {renderedMenuEntries}\n </MuiMenu>\n );\n}\n","import type { FC, ReactNode, MouseEvent, KeyboardEvent } from 'react';\nimport { Children, cloneElement, isValidElement, useCallback, useId, useRef, useState } from 'react';\nimport Fade from '@mui/material/Fade';\nimport type { MenuItemProps } from '@mui/material/MenuItem';\nimport MuiMenuItem from '@mui/material/MenuItem';\nimport Divider from '@mui/material/Divider';\nimport ArrowRightIcon from '@mui/icons-material/ArrowRight';\nimport type { SvgIconComponent } from '@mui/icons-material';\nimport { MenuList, Paper, Popper, Typography } from '@mui/material';\nimport type { MenuItem } from './types';\nimport { CLOSE_DELAY, MenuItemContent, transitionConfig } from './common';\n\ntype NestedMenuItemProps = MenuItemProps & {\n label: ReactNode;\n startIcon?: SvgIconComponent;\n endIcon?: SvgIconComponent;\n parentMenuClose: () => void;\n children?: ReactNode;\n items?: MenuItem[];\n};\n\nconst isNodeInstance = (target: EventTarget | null): target is Node => target instanceof Node;\n\nexport const NestedMenuItem: FC<NestedMenuItemProps> = props => {\n const {\n id: providedId,\n label,\n startIcon: StartIconComponent,\n parentMenuClose,\n children,\n items,\n endIcon: _,\n ...menuItemProps\n } = props;\n const [subMenuAnchorEl, setSubMenuAnchorEl] = useState<null | HTMLElement>(null);\n const open = Boolean(subMenuAnchorEl);\n const menuItemRef = useRef<HTMLLIElement>(null);\n const subMenuRef = useRef<HTMLDivElement>(null);\n const generatedId = useId();\n const menuItemId = providedId ?? `nested-menu-trigger-${generatedId}`;\n const subMenuId = `${menuItemId}-submenu`;\n\n const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const clearCloseTimer = useCallback(() => {\n if (closeTimerRef.current) {\n clearTimeout(closeTimerRef.current);\n closeTimerRef.current = null;\n }\n }, []);\n\n const handleClose = useCallback(() => {\n clearCloseTimer();\n setSubMenuAnchorEl(null);\n }, [clearCloseTimer]);\n\n const scheduleClose = useCallback(() => {\n clearCloseTimer();\n closeTimerRef.current = setTimeout(() => {\n handleClose();\n }, CLOSE_DELAY);\n }, [clearCloseTimer, handleClose]);\n\n const handleOpen = (event: MouseEvent<HTMLLIElement> | KeyboardEvent<HTMLLIElement>) => {\n clearCloseTimer();\n setSubMenuAnchorEl(event.currentTarget);\n };\n\n\n // eslint-disable-next-line react-hooks/refs\n const renderChildren = Children.map(children, child => {\n if (!isValidElement(child)) return child;\n\n // Ensure we only process MUI MenuItem children\n if (child.type === MuiMenuItem) {\n const childOnClick = (child.props as MenuItemProps).onClick;\n // Merge any user-defined click logic with the submenu closing behavior.\n const clonedOnClick = (event: MouseEvent<HTMLLIElement>) => {\n childOnClick?.(event);\n handleClose(); // Close the submenu\n parentMenuClose();\n };\n return cloneElement(child, { onClick: clonedOnClick } as Partial<MenuItemProps>);\n }\n return child;\n });\n\n const renderItemsFromData = () => {\n if (!items || items.length === 0) return null;\n\n return items.map((item, index) => {\n if (item.type === 'divider') {\n\n return <Divider key={`divider-${index}`} />;\n }\n\n const {\n type: __,\n items: entryItems,\n startIcon: NestedMenuItemStartIcon,\n endIcon: NestedMenuItemEndIcon,\n label: entryLabel,\n onClick,\n id,\n } = item;\n const entryId = id ?? `${menuItemId}-entry-${index}`;\n const entryKey = `nested-entry-${entryId}`;\n const entryLabelValue = entryLabel ?? entryId;\n\n if (entryItems && entryItems.length > 0) {\n return (\n <NestedMenuItem\n key={entryKey}\n id={entryId}\n label={entryLabelValue}\n startIcon={NestedMenuItemStartIcon}\n endIcon={NestedMenuItemEndIcon}\n parentMenuClose={parentMenuClose}\n items={entryItems}\n />\n );\n }\n\n const handleItemClick = (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n handleClose();\n parentMenuClose();\n };\n\n return (\n <MuiMenuItem key={entryKey} onClick={handleItemClick}>\n <MenuItemContent>\n {NestedMenuItemStartIcon ? <NestedMenuItemStartIcon /> : null}\n <Typography sx={{ flex: 1 }}>{entryLabelValue}</Typography>\n {NestedMenuItemEndIcon ? <NestedMenuItemEndIcon /> : null}\n </MenuItemContent>\n </MuiMenuItem>\n );\n });\n };\n\n const renderedSubMenuItems = items && items.length > 0 ? renderItemsFromData() : renderChildren;\n\n return (\n <>\n <MuiMenuItem\n data-testid={`${menuItemId}-trigger`}\n id={menuItemId}\n ref={menuItemRef}\n onMouseEnter={handleOpen}\n onMouseLeave={e => {\n // CRITICAL FEATURE:\n // Checking whether cursor left the menu item onto the related menu. If so, do not close.\n // TODO(ege): There can be a timeout here before we execute closing to improve UX - in case user is not very precise with mouse.\n if (isNodeInstance(e.relatedTarget) && subMenuRef.current?.contains(e.relatedTarget)) return;\n // If the cursor leaves to anywhere else, close the submenu.\n scheduleClose();\n }}\n onKeyDown={e => {\n e.preventDefault();\n if (e.key === 'ArrowLeft') {\n handleClose();\n }\n if (e.key === 'ArrowRight' || e.key === 'Enter' || e.key === ' ') {\n handleOpen(e);\n }\n }}\n aria-haspopup='menu'\n aria-controls={subMenuId}\n aria-expanded={open ? 'true' : undefined}\n {...menuItemProps}\n >\n <MenuItemContent>\n {StartIconComponent ? <StartIconComponent /> : null}\n <Typography sx={{ flex: 1 }}>{label}</Typography>\n <ArrowRightIcon />\n </MenuItemContent>\n </MuiMenuItem>\n\n <Popper\n data-testid={`${menuItemId}-submenu`}\n open={open}\n ref={subMenuRef}\n anchorEl={subMenuAnchorEl}\n transition\n sx={{ zIndex: t => t.zIndex.modal + 1 }}\n placement='right-start'\n onKeyDown={e => {\n if (e.key === 'ArrowLeft') {\n e.preventDefault();\n handleClose();\n menuItemRef.current?.focus();\n }\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n e.preventDefault();\n e.stopPropagation();\n }\n }}\n onMouseEnter={clearCloseTimer}\n onMouseLeave={e => {\n // CRITICAL FEATURE:\n // Checking whether cursor left the submenu onto the related trigger item. If so, do not close.\n // TODO(ege): There can be a timeout here before we execute closing to improve UX - in case user is not very precise with mouse.\n if (isNodeInstance(e.relatedTarget) && menuItemRef.current?.contains(e.relatedTarget)) return;\n // If the cursor leaves to anywhere else, close the submenu.\n scheduleClose();\n }}\n >\n {({ TransitionProps }) => (\n <Fade {...TransitionProps} timeout={transitionConfig.timeout}>\n <Paper\n elevation={2}\n sx={{\n borderRadius: 1,\n bgcolor: 'background.default',\n }}\n >\n <MenuList\n autoFocusItem\n id={subMenuId}\n aria-labelledby={menuItemId}\n role='menu'\n onKeyDown={e => {\n if (e.key === 'ArrowLeft') {\n const { nativeEvent } = e as KeyboardEvent<HTMLUListElement> & {\n nativeEvent: KeyboardEvent & {\n // our custom flag to avoid duplicate handling in nested menus\n __nestedMenuArrowLeftHandled?: boolean;\n };\n };\n if (nativeEvent.__nestedMenuArrowLeftHandled) {\n // return early if we have already handled this event in a nested menu\n // prevents duplicate logic when the browser bubbles the same keypress through multiple nodes\n return;\n }\n if (!subMenuRef.current?.contains(e.target as Node)) {\n // confirm the event’s target is still inside this submenu;\n // if the keypress originated elsewhere, we don’t continue so other menus can handle it\n return;\n }\n // Mark this event as handled to prevent parent menus from also processing it\n nativeEvent.__nestedMenuArrowLeftHandled = true;\n // preventDefault is needed so the browser doesn’t do its own “arrow left” behavior (like moving the text caret\n // or changing focus) while the menu is managing the navigation. Without this, the closed submenu or focused item\n // might jump unexpectedly even though the menu logic is running.\n e.preventDefault();\n // stopPropagation stops the event from bubbling up to parent DOM nodes that might also have onKeyDown, which could\n // otherwise fight the menu’s own logic or trigger duplicate navigation even though we guard with __nestedMenuArrowLeftHandled\n e.stopPropagation();\n // stopImmediatePropagation keeps any other keydown handlers registered on this same DOM node from running after ours.\n // This is preventing all submenus to close when pressing ArrowLeft in a, say, third level menu. With this, ArrowLeft will only\n // close the current submenu. The __nestedMenuArrowLeftHandled flag only guards against bubbling; this stopImmediatePropagation()\n // ensures no other handlers wired to the same node interfere once we decide to close and focus.\n nativeEvent.stopImmediatePropagation();\n // close the sub menu\n handleClose();\n // move focus back to the parent MenuItem, letting the user continue navigation up the menu hierarchy\n menuItemRef.current?.focus();\n }\n }}\n >\n {renderedSubMenuItems}\n </MenuList>\n </Paper>\n </Fade>\n )}\n </Popper>\n </>\n );\n};\n","'use client';\n\nexport { createSvgIcon as default } from '@mui/material/utils';","\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.js\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"m10 17 5-5-5-5z\"\n}), 'ArrowRight');","import { Stack, styled } from '@mui/material';\nimport Fade from '@mui/material/Fade';\n\nexport const CLOSE_DELAY = 50;\nexport const transitionConfig = {\n type: Fade,\n timeout: { enter: CLOSE_DELAY + 50, exit: CLOSE_DELAY + 50 },\n};\n\nexport const MenuItemContent = styled(Stack)(({ theme }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.spacing(1),\n fontSize: theme.typography.body1.fontSize,\n '& .MuiSvgIcon-root': {\n fontSize: theme.typography.body2.fontSize,\n },\n padding: 0,\n}));\n"],"mappings":"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","names":["import_Divider","import_MenuItem","import_react","import_material","import_Fade","_jsx","import_material","Fade","import_jsx_runtime","MuiMenuItem","Divider","Fade","import_jsx_runtime","Divider","MuiMenuItem","MuiMenu"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Menu/index.tsx","../src/Menu/NestedMenuItem.tsx","../../../node_modules/@mui/icons-material/esm/utils/createSvgIcon.js","../../../node_modules/@mui/icons-material/esm/ArrowRight.js","../src/Menu/common.ts"],"sourcesContent":["import { Menu } from './Menu'\n\nexport type { MenuItem } from './Menu/types'\nexport default Menu\n","import type { MenuProps } from '@mui/material/Menu';\nimport MuiMenu from '@mui/material/Menu';\nimport Divider from '@mui/material/Divider';\nimport MuiMenuItem from '@mui/material/MenuItem';\nimport React, { useId } from 'react';\nimport { Typography } from '@mui/material';\nimport { NestedMenuItem } from './NestedMenuItem';\nimport type { MenuItem } from './types';\nimport { DEFAULT_ELEVATION, MenuItemContent, transitionConfig } from './common';\n\nexport type Props = {\n items: MenuItem[];\n onClose?: (event: React.MouseEvent | React.KeyboardEvent, reason: 'itemClick' | 'escapeKeyDown' | 'backdropClick', menuItemId?: string\n ) => void;\n} & Omit<MenuProps, 'onClose'>;\n\nexport function Menu({ items, elevation = DEFAULT_ELEVATION, ...rest }: Props) {\n const menuProps: Omit<Props, 'items'> = { elevation, ...rest }; // setting the elevation for all nested menus here\n const generatedMenuId = useId();\n\n const renderedMenuEntries = items.map((item, index) => {\n if (item.type === 'divider') {\n return <Divider key={`divider-${index}`} />;\n }\n\n const {\n type: _,\n items: nestedItems,\n startIcon: StartIconComponent,\n endIcon: EndIconComponent,\n label,\n onClick,\n id,\n ...menuItemProps\n } = item;\n const entryId = id ?? `${generatedMenuId}-entry-${index}`;\n const itemKey = `menu-item-id:${entryId}`;\n const displayLabel = label ?? entryId;\n\n if (nestedItems && nestedItems.length > 0) {\n return (\n <NestedMenuItem\n key={itemKey}\n id={entryId}\n label={displayLabel}\n startIcon={StartIconComponent}\n endIcon={EndIconComponent}\n parentMenuClose={menuProps.onClose}\n items={nestedItems}\n menuProps={menuProps}\n />\n );\n }\n\n const handleItemClick = (event: React.MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n menuProps.onClose?.(event, \"itemClick\", entryId);\n };\n\n return (\n <MuiMenuItem key={itemKey} onClick={handleItemClick} {...menuItemProps}>\n <MenuItemContent>\n {StartIconComponent ? <StartIconComponent /> : null}\n <Typography sx={{ flex: 1 }}>{displayLabel}</Typography>\n {EndIconComponent ? <EndIconComponent /> : null}\n </MenuItemContent>\n </MuiMenuItem>\n );\n });\n\n return (\n <MuiMenu\n data-testid='root-menu'\n {...menuProps}\n transitionDuration={menuProps.transitionDuration || transitionConfig.timeout}\n slots={{ transition: transitionConfig.type, ...menuProps.slots }}\n >\n {renderedMenuEntries}\n </MuiMenu>\n );\n}\n","import type { FC, ReactNode, MouseEvent, KeyboardEvent } from 'react';\nimport { Children, cloneElement, isValidElement, useCallback, useId, useRef, useState } from 'react';\nimport Fade from '@mui/material/Fade';\nimport type { MenuItemProps } from '@mui/material/MenuItem';\nimport MuiMenuItem from '@mui/material/MenuItem';\nimport Divider from '@mui/material/Divider';\nimport ArrowRightIcon from '@mui/icons-material/ArrowRight';\nimport type { SvgIconComponent } from '@mui/icons-material';\nimport type { MenuListProps, MenuProps, PaperProps } from '@mui/material';\nimport { MenuList, Paper, Popper, Typography } from '@mui/material';\nimport type { MenuItem } from './types';\nimport { CLOSE_DELAY, MenuItemContent, transitionConfig } from './common';\nimport type { Props as BetterMenuProps } from '.';\n\ntype NestedMenuItemProps = MenuItemProps & {\n label: ReactNode;\n startIcon?: SvgIconComponent;\n endIcon?: SvgIconComponent;\n parentMenuClose: BetterMenuProps['onClose'];\n children?: ReactNode;\n items?: MenuItem[];\n menuProps: MenuProps;\n};\n\nconst isNodeInstance = (target: EventTarget | null): target is Node => target instanceof Node;\n\nexport const NestedMenuItem: FC<NestedMenuItemProps> = props => {\n const {\n id: providedId,\n label,\n startIcon: StartIconComponent,\n parentMenuClose,\n children,\n items,\n endIcon: _,\n menuProps,\n ...menuItemProps\n } = props;\n const [subMenuAnchorEl, setSubMenuAnchorEl] = useState<null | HTMLElement>(null);\n const open = Boolean(subMenuAnchorEl);\n const menuItemRef = useRef<HTMLLIElement>(null);\n const subMenuRef = useRef<HTMLDivElement>(null);\n const generatedId = useId();\n const menuItemId = providedId ?? `nested-menu-trigger-${generatedId}`;\n const subMenuId = `${menuItemId}-submenu`;\n\n const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const clearCloseTimer = useCallback(() => {\n if (closeTimerRef.current) {\n clearTimeout(closeTimerRef.current);\n closeTimerRef.current = null;\n }\n }, []);\n\n const handleClose = useCallback(() => {\n clearCloseTimer();\n setSubMenuAnchorEl(null);\n }, [clearCloseTimer]);\n\n const scheduleClose = useCallback(() => {\n clearCloseTimer();\n closeTimerRef.current = setTimeout(() => {\n handleClose();\n }, CLOSE_DELAY);\n }, [clearCloseTimer, handleClose]);\n\n const handleOpen = (event: MouseEvent<HTMLLIElement> | KeyboardEvent<HTMLLIElement>) => {\n clearCloseTimer();\n setSubMenuAnchorEl(event.currentTarget);\n };\n\n\n // eslint-disable-next-line react-hooks/refs\n const renderChildren = Children.map(children, child => {\n if (!isValidElement(child)) return child;\n\n // Ensure we only process MUI MenuItem children\n if (child.type === MuiMenuItem) {\n const childOnClick = (child.props as MenuItemProps).onClick;\n // Merge any user-defined click logic with the submenu closing behavior.\n const clonedOnClick = (event: MouseEvent<HTMLLIElement>) => {\n childOnClick?.(event);\n handleClose(); // Close the submenu\n parentMenuClose?.(event, \"itemClick\", menuItemId);\n };\n return cloneElement(child, { onClick: clonedOnClick } as Partial<MenuItemProps>);\n }\n return child;\n });\n\n const renderItemsFromData = () => {\n if (!items || items.length === 0) return null;\n\n return items.map((item, index) => {\n if (item.type === 'divider') {\n\n return <Divider key={`divider-${index}`} />;\n }\n\n const {\n type: __,\n items: entryItems,\n startIcon: NestedMenuItemStartIcon,\n endIcon: NestedMenuItemEndIcon,\n label: entryLabel,\n onClick,\n id,\n } = item;\n const entryId = id ?? `${menuItemId}-entry-${index}`;\n const entryKey = `nested-entry-${entryId}`;\n const entryLabelValue = entryLabel ?? entryId;\n\n if (entryItems && entryItems.length > 0) {\n return (\n <NestedMenuItem\n key={entryKey}\n id={entryId}\n label={entryLabelValue}\n startIcon={NestedMenuItemStartIcon}\n endIcon={NestedMenuItemEndIcon}\n parentMenuClose={parentMenuClose}\n items={entryItems}\n menuProps={menuProps}\n />\n );\n }\n\n const handleItemClick = (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n handleClose();\n parentMenuClose?.(event, \"itemClick\", entryId);\n };\n\n return (\n <MuiMenuItem key={entryKey} onClick={handleItemClick}>\n <MenuItemContent>\n {NestedMenuItemStartIcon ? <NestedMenuItemStartIcon /> : null}\n <Typography sx={{ flex: 1 }}>{entryLabelValue}</Typography>\n {NestedMenuItemEndIcon ? <NestedMenuItemEndIcon /> : null}\n </MenuItemContent>\n </MuiMenuItem>\n );\n });\n };\n\n const renderedSubMenuItems = items && items.length > 0 ? renderItemsFromData() : renderChildren;\n\n return (\n <>\n <MuiMenuItem\n data-testid={`${menuItemId}-trigger`}\n id={menuItemId}\n ref={menuItemRef}\n onMouseEnter={handleOpen}\n onMouseLeave={e => {\n // CRITICAL FEATURE:\n // Checking whether cursor left the menu item onto the related menu. If so, do not close.\n if (isNodeInstance(e.relatedTarget) && subMenuRef.current?.contains(e.relatedTarget)) return;\n // If the cursor leaves to anywhere else, close the submenu.\n scheduleClose();\n }}\n onKeyDown={e => {\n e.preventDefault();\n if (e.key === 'ArrowLeft') {\n handleClose();\n }\n if (e.key === 'ArrowRight' || e.key === 'Enter' || e.key === ' ') {\n handleOpen(e);\n }\n }}\n aria-haspopup='menu'\n aria-controls={subMenuId}\n aria-expanded={open ? 'true' : undefined}\n {...menuItemProps}\n >\n <MenuItemContent>\n {StartIconComponent ? <StartIconComponent /> : null}\n <Typography sx={{ flex: 1 }}>{label}</Typography>\n <ArrowRightIcon />\n </MenuItemContent>\n </MuiMenuItem>\n\n {/**\n * CRITICAL FEATURE: Menu over Menu fails with focus management and keyboard navigations. \n * It is way more complex to make Menu to work compared to Popper. So, I made the decision to use Popper for submenus.\n * This way we can manage focus and keyboard navigation simpler, and we can also avoid some weird edge cases.\n * If you change this to regular Menu, you will realize that you need to add many custom JS logic to manage focus and keyboard navigation,\n * as well as to block Menu's internal logic.\n */}\n <Popper\n data-testid={`${menuItemId}-submenu`}\n open={open}\n ref={subMenuRef}\n anchorEl={subMenuAnchorEl}\n transition\n sx={{ zIndex: t => t.zIndex.modal + 1 }}\n placement='right-start'\n onKeyDown={e => {\n if (e.key === 'ArrowLeft') {\n e.preventDefault();\n handleClose();\n menuItemRef.current?.focus();\n }\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n e.preventDefault();\n e.stopPropagation();\n }\n }}\n onMouseEnter={clearCloseTimer}\n onMouseLeave={e => {\n // CRITICAL FEATURE:\n // Checking whether cursor left the submenu onto the related trigger item. If so, do not close.\n if (isNodeInstance(e.relatedTarget) && menuItemRef.current?.contains(e.relatedTarget)) return;\n // If the cursor leaves to anywhere else, close the submenu.\n scheduleClose();\n }}\n >\n {({ TransitionProps }) => (\n <Fade {...TransitionProps} timeout={transitionConfig.timeout}>\n <Paper elevation={menuProps.elevation} {...(menuProps?.slotProps?.paper as PaperProps) || {}}>\n <MenuList\n id={subMenuId}\n aria-labelledby={menuItemId}\n role='menu'\n {...(menuProps?.slotProps?.list as MenuListProps) || {}}\n // What's under is not allowed to be overridden for now\n autoFocusItem\n onKeyDown={e => {\n if (e.key === 'ArrowLeft') {\n const { nativeEvent } = e as KeyboardEvent<HTMLUListElement> & {\n nativeEvent: KeyboardEvent & {\n // our custom flag to avoid duplicate handling in nested menus\n __nestedMenuArrowLeftHandled?: boolean;\n };\n };\n if (nativeEvent.__nestedMenuArrowLeftHandled) {\n // return early if we have already handled this event in a nested menu\n // prevents duplicate logic when the browser bubbles the same keypress through multiple nodes\n return;\n }\n if (!subMenuRef.current?.contains(e.target as Node)) {\n // confirm the event’s target is still inside this submenu;\n // if the keypress originated elsewhere, we don’t continue so other menus can handle it\n return;\n }\n // Mark this event as handled to prevent parent menus from also processing it\n nativeEvent.__nestedMenuArrowLeftHandled = true;\n // preventDefault is needed so the browser doesn’t do its own “arrow left” behavior (like moving the text caret\n // or changing focus) while the menu is managing the navigation. Without this, the closed submenu or focused item\n // might jump unexpectedly even though the menu logic is running.\n e.preventDefault();\n // stopPropagation stops the event from bubbling up to parent DOM nodes that might also have onKeyDown, which could\n // otherwise fight the menu’s own logic or trigger duplicate navigation even though we guard with __nestedMenuArrowLeftHandled\n e.stopPropagation();\n // stopImmediatePropagation keeps any other keydown handlers registered on this same DOM node from running after ours.\n // This is preventing all submenus to close when pressing ArrowLeft in a, say, third level menu. With this, ArrowLeft will only\n // close the current submenu. The __nestedMenuArrowLeftHandled flag only guards against bubbling; this stopImmediatePropagation()\n // ensures no other handlers wired to the same node interfere once we decide to close and focus.\n nativeEvent.stopImmediatePropagation();\n // close the sub menu\n handleClose();\n // move focus back to the parent MenuItem, letting the user continue navigation up the menu hierarchy\n menuItemRef.current?.focus();\n }\n }}\n >\n {renderedSubMenuItems}\n </MenuList>\n </Paper>\n </Fade>\n )}\n </Popper>\n </>\n );\n};\n","'use client';\n\nexport { createSvgIcon as default } from '@mui/material/utils';","\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.js\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"m10 17 5-5-5-5z\"\n}), 'ArrowRight');","import { Stack, styled } from '@mui/material';\nimport Fade from '@mui/material/Fade';\n\nexport const CLOSE_DELAY = 50;\nexport const transitionConfig = {\n type: Fade,\n timeout: { enter: CLOSE_DELAY + 50, exit: CLOSE_DELAY + 50 },\n};\nexport const DEFAULT_ELEVATION = 8; // this is also what MUI menu uses as default\n\nexport const MenuItemContent = styled(Stack)({\n flexDirection: 'row',\n alignItems: 'center',\n gap: '8px',\n padding: 0,\n});\n"],"mappings":"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","names":["import_Divider","import_MenuItem","import_react","import_material","import_Fade","_jsx","import_material","Fade","import_jsx_runtime","MuiMenuItem","Divider","Fade","import_jsx_runtime","Divider","MuiMenuItem","MuiMenu"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { MenuProps } from '@mui/material/Menu';
|
|
3
|
+
import React, { ReactNode } from 'react';
|
|
3
4
|
import { MenuItemProps } from '@mui/material/MenuItem';
|
|
4
5
|
import { SvgIconComponent } from '@mui/icons-material';
|
|
5
6
|
|
|
@@ -16,11 +17,9 @@ type MenuItemBase = {
|
|
|
16
17
|
type MenuItem = MenuItemBase & Omit<MenuItemProps, 'children'>;
|
|
17
18
|
|
|
18
19
|
type Props = {
|
|
19
|
-
anchorEl: null | HTMLElement;
|
|
20
|
-
open: boolean;
|
|
21
|
-
onClose: () => void;
|
|
22
20
|
items: MenuItem[];
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
onClose?: (event: React.MouseEvent | React.KeyboardEvent, reason: 'itemClick' | 'escapeKeyDown' | 'backdropClick', menuItemId?: string) => void;
|
|
22
|
+
} & Omit<MenuProps, 'onClose'>;
|
|
23
|
+
declare function Menu({ items, elevation, ...rest }: Props): react_jsx_runtime.JSX.Element;
|
|
25
24
|
|
|
26
25
|
export { type MenuItem, Menu as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { MenuProps } from '@mui/material/Menu';
|
|
3
|
+
import React, { ReactNode } from 'react';
|
|
3
4
|
import { MenuItemProps } from '@mui/material/MenuItem';
|
|
4
5
|
import { SvgIconComponent } from '@mui/icons-material';
|
|
5
6
|
|
|
@@ -16,11 +17,9 @@ type MenuItemBase = {
|
|
|
16
17
|
type MenuItem = MenuItemBase & Omit<MenuItemProps, 'children'>;
|
|
17
18
|
|
|
18
19
|
type Props = {
|
|
19
|
-
anchorEl: null | HTMLElement;
|
|
20
|
-
open: boolean;
|
|
21
|
-
onClose: () => void;
|
|
22
20
|
items: MenuItem[];
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
onClose?: (event: React.MouseEvent | React.KeyboardEvent, reason: 'itemClick' | 'escapeKeyDown' | 'backdropClick', menuItemId?: string) => void;
|
|
22
|
+
} & Omit<MenuProps, 'onClose'>;
|
|
23
|
+
declare function Menu({ items, elevation, ...rest }: Props): react_jsx_runtime.JSX.Element;
|
|
25
24
|
|
|
26
25
|
export { type MenuItem, Menu as default };
|
package/dist/index.js
CHANGED
|
@@ -31,16 +31,13 @@ var transitionConfig = {
|
|
|
31
31
|
type: Fade,
|
|
32
32
|
timeout: { enter: CLOSE_DELAY + 50, exit: CLOSE_DELAY + 50 }
|
|
33
33
|
};
|
|
34
|
-
var
|
|
34
|
+
var DEFAULT_ELEVATION = 8;
|
|
35
|
+
var MenuItemContent = styled(Stack)({
|
|
35
36
|
flexDirection: "row",
|
|
36
37
|
alignItems: "center",
|
|
37
|
-
gap:
|
|
38
|
-
fontSize: theme.typography.body1.fontSize,
|
|
39
|
-
"& .MuiSvgIcon-root": {
|
|
40
|
-
fontSize: theme.typography.body2.fontSize
|
|
41
|
-
},
|
|
38
|
+
gap: "8px",
|
|
42
39
|
padding: 0
|
|
43
|
-
})
|
|
40
|
+
});
|
|
44
41
|
|
|
45
42
|
// src/Menu/NestedMenuItem.tsx
|
|
46
43
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -54,6 +51,7 @@ var NestedMenuItem = (props) => {
|
|
|
54
51
|
children,
|
|
55
52
|
items,
|
|
56
53
|
endIcon: _,
|
|
54
|
+
menuProps,
|
|
57
55
|
...menuItemProps
|
|
58
56
|
} = props;
|
|
59
57
|
const [subMenuAnchorEl, setSubMenuAnchorEl] = useState(null);
|
|
@@ -91,7 +89,7 @@ var NestedMenuItem = (props) => {
|
|
|
91
89
|
const clonedOnClick = (event) => {
|
|
92
90
|
childOnClick?.(event);
|
|
93
91
|
handleClose();
|
|
94
|
-
parentMenuClose();
|
|
92
|
+
parentMenuClose?.(event, "itemClick", menuItemId);
|
|
95
93
|
};
|
|
96
94
|
return cloneElement(child, { onClick: clonedOnClick });
|
|
97
95
|
}
|
|
@@ -124,7 +122,8 @@ var NestedMenuItem = (props) => {
|
|
|
124
122
|
startIcon: NestedMenuItemStartIcon,
|
|
125
123
|
endIcon: NestedMenuItemEndIcon,
|
|
126
124
|
parentMenuClose,
|
|
127
|
-
items: entryItems
|
|
125
|
+
items: entryItems,
|
|
126
|
+
menuProps
|
|
128
127
|
},
|
|
129
128
|
entryKey
|
|
130
129
|
);
|
|
@@ -132,7 +131,7 @@ var NestedMenuItem = (props) => {
|
|
|
132
131
|
const handleItemClick = (event) => {
|
|
133
132
|
onClick?.(event);
|
|
134
133
|
handleClose();
|
|
135
|
-
parentMenuClose();
|
|
134
|
+
parentMenuClose?.(event, "itemClick", entryId);
|
|
136
135
|
};
|
|
137
136
|
return /* @__PURE__ */ jsx(MuiMenuItem, { onClick: handleItemClick, children: /* @__PURE__ */ jsxs(MenuItemContent, { children: [
|
|
138
137
|
NestedMenuItemStartIcon ? /* @__PURE__ */ jsx(NestedMenuItemStartIcon, {}) : null,
|
|
@@ -200,43 +199,34 @@ var NestedMenuItem = (props) => {
|
|
|
200
199
|
if (isNodeInstance(e.relatedTarget) && menuItemRef.current?.contains(e.relatedTarget)) return;
|
|
201
200
|
scheduleClose();
|
|
202
201
|
},
|
|
203
|
-
children: ({ TransitionProps }) => /* @__PURE__ */ jsx(Fade2, { ...TransitionProps, timeout: transitionConfig.timeout, children: /* @__PURE__ */ jsx(
|
|
204
|
-
|
|
202
|
+
children: ({ TransitionProps }) => /* @__PURE__ */ jsx(Fade2, { ...TransitionProps, timeout: transitionConfig.timeout, children: /* @__PURE__ */ jsx(Paper, { elevation: menuProps.elevation, ...menuProps?.slotProps?.paper || {}, children: /* @__PURE__ */ jsx(
|
|
203
|
+
MenuList,
|
|
205
204
|
{
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
nativeEvent.__nestedMenuArrowLeftHandled = true;
|
|
228
|
-
e.preventDefault();
|
|
229
|
-
e.stopPropagation();
|
|
230
|
-
nativeEvent.stopImmediatePropagation();
|
|
231
|
-
handleClose();
|
|
232
|
-
menuItemRef.current?.focus();
|
|
233
|
-
}
|
|
234
|
-
},
|
|
235
|
-
children: renderedSubMenuItems
|
|
205
|
+
id: subMenuId,
|
|
206
|
+
"aria-labelledby": menuItemId,
|
|
207
|
+
role: "menu",
|
|
208
|
+
...menuProps?.slotProps?.list || {},
|
|
209
|
+
autoFocusItem: true,
|
|
210
|
+
onKeyDown: (e) => {
|
|
211
|
+
if (e.key === "ArrowLeft") {
|
|
212
|
+
const { nativeEvent } = e;
|
|
213
|
+
if (nativeEvent.__nestedMenuArrowLeftHandled) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (!subMenuRef.current?.contains(e.target)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
nativeEvent.__nestedMenuArrowLeftHandled = true;
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
e.stopPropagation();
|
|
222
|
+
nativeEvent.stopImmediatePropagation();
|
|
223
|
+
handleClose();
|
|
224
|
+
menuItemRef.current?.focus();
|
|
236
225
|
}
|
|
237
|
-
|
|
226
|
+
},
|
|
227
|
+
children: renderedSubMenuItems
|
|
238
228
|
}
|
|
239
|
-
) })
|
|
229
|
+
) }) })
|
|
240
230
|
}
|
|
241
231
|
)
|
|
242
232
|
] });
|
|
@@ -244,7 +234,8 @@ var NestedMenuItem = (props) => {
|
|
|
244
234
|
|
|
245
235
|
// src/Menu/index.tsx
|
|
246
236
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
247
|
-
function Menu({
|
|
237
|
+
function Menu({ items, elevation = DEFAULT_ELEVATION, ...rest }) {
|
|
238
|
+
const menuProps = { elevation, ...rest };
|
|
248
239
|
const generatedMenuId = useId2();
|
|
249
240
|
const renderedMenuEntries = items.map((item, index) => {
|
|
250
241
|
if (item.type === "divider") {
|
|
@@ -271,15 +262,16 @@ function Menu({ anchorEl, open, onClose, items }) {
|
|
|
271
262
|
label: displayLabel,
|
|
272
263
|
startIcon: StartIconComponent,
|
|
273
264
|
endIcon: EndIconComponent,
|
|
274
|
-
parentMenuClose: onClose,
|
|
275
|
-
items: nestedItems
|
|
265
|
+
parentMenuClose: menuProps.onClose,
|
|
266
|
+
items: nestedItems,
|
|
267
|
+
menuProps
|
|
276
268
|
},
|
|
277
269
|
itemKey
|
|
278
270
|
);
|
|
279
271
|
}
|
|
280
272
|
const handleItemClick = (event) => {
|
|
281
273
|
onClick?.(event);
|
|
282
|
-
onClose();
|
|
274
|
+
menuProps.onClose?.(event, "itemClick", entryId);
|
|
283
275
|
};
|
|
284
276
|
return /* @__PURE__ */ jsx2(MuiMenuItem2, { onClick: handleItemClick, ...menuItemProps, children: /* @__PURE__ */ jsxs2(MenuItemContent, { children: [
|
|
285
277
|
StartIconComponent ? /* @__PURE__ */ jsx2(StartIconComponent, {}) : null,
|
|
@@ -291,19 +283,9 @@ function Menu({ anchorEl, open, onClose, items }) {
|
|
|
291
283
|
MuiMenu,
|
|
292
284
|
{
|
|
293
285
|
"data-testid": "root-menu",
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
onKeyDown: (e) => {
|
|
298
|
-
if (e.key === "Escape" && open) onClose();
|
|
299
|
-
},
|
|
300
|
-
transitionDuration: transitionConfig.timeout,
|
|
301
|
-
slots: { transition: transitionConfig.type },
|
|
302
|
-
slotProps: {
|
|
303
|
-
list: {
|
|
304
|
-
"aria-labelledby": "icon-menu-button"
|
|
305
|
-
}
|
|
306
|
-
},
|
|
286
|
+
...menuProps,
|
|
287
|
+
transitionDuration: menuProps.transitionDuration || transitionConfig.timeout,
|
|
288
|
+
slots: { transition: transitionConfig.type, ...menuProps.slots },
|
|
307
289
|
children: renderedMenuEntries
|
|
308
290
|
}
|
|
309
291
|
);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Menu/index.tsx","../src/Menu/NestedMenuItem.tsx","../../../node_modules/@mui/icons-material/esm/utils/createSvgIcon.js","../../../node_modules/@mui/icons-material/esm/ArrowRight.js","../src/Menu/common.ts","../src/index.ts"],"sourcesContent":["import MuiMenu from '@mui/material/Menu';\nimport Divider from '@mui/material/Divider';\nimport MuiMenuItem from '@mui/material/MenuItem';\nimport React, { useId } from 'react';\nimport { Typography } from '@mui/material';\nimport { NestedMenuItem } from './NestedMenuItem';\nimport type { MenuItem } from './types';\nimport { MenuItemContent, transitionConfig } from './common';\n\ntype Props = {\n anchorEl: null | HTMLElement;\n open: boolean;\n onClose: () => void;\n items: MenuItem[];\n};\n\nexport function Menu({ anchorEl, open, onClose, items }: Props) {\n const generatedMenuId = useId();\n\n const renderedMenuEntries = items.map((item, index) => {\n if (item.type === 'divider') {\n return <Divider key={`divider-${index}`} />;\n }\n\n const {\n type: _,\n items: nestedItems,\n startIcon: StartIconComponent,\n endIcon: EndIconComponent,\n label,\n onClick,\n id,\n ...menuItemProps\n } = item;\n const entryId = id ?? `${generatedMenuId}-entry-${index}`;\n const itemKey = `menu-item-id:${entryId}`;\n const displayLabel = label ?? entryId;\n\n if (nestedItems && nestedItems.length > 0) {\n return (\n <NestedMenuItem\n key={itemKey}\n id={entryId}\n label={displayLabel}\n startIcon={StartIconComponent}\n endIcon={EndIconComponent}\n parentMenuClose={onClose}\n items={nestedItems}\n />\n );\n }\n\n const handleItemClick = (event: React.MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n onClose();\n };\n\n return (\n <MuiMenuItem key={itemKey} onClick={handleItemClick} {...menuItemProps}>\n <MenuItemContent>\n {StartIconComponent ? <StartIconComponent /> : null}\n <Typography sx={{ flex: 1 }}>{displayLabel}</Typography>\n {EndIconComponent ? <EndIconComponent /> : null}\n </MenuItemContent>\n </MuiMenuItem>\n );\n });\n\n return (\n <MuiMenu\n data-testid='root-menu'\n anchorEl={anchorEl}\n open={open}\n onClose={onClose}\n onKeyDown={e => {\n if (e.key === 'Escape' && open) onClose();\n }}\n transitionDuration={transitionConfig.timeout}\n slots={{ transition: transitionConfig.type }}\n slotProps={{\n list: {\n 'aria-labelledby': 'icon-menu-button',\n },\n }}\n >\n {renderedMenuEntries}\n </MuiMenu>\n );\n}\n","import type { FC, ReactNode, MouseEvent, KeyboardEvent } from 'react';\nimport { Children, cloneElement, isValidElement, useCallback, useId, useRef, useState } from 'react';\nimport Fade from '@mui/material/Fade';\nimport type { MenuItemProps } from '@mui/material/MenuItem';\nimport MuiMenuItem from '@mui/material/MenuItem';\nimport Divider from '@mui/material/Divider';\nimport ArrowRightIcon from '@mui/icons-material/ArrowRight';\nimport type { SvgIconComponent } from '@mui/icons-material';\nimport { MenuList, Paper, Popper, Typography } from '@mui/material';\nimport type { MenuItem } from './types';\nimport { CLOSE_DELAY, MenuItemContent, transitionConfig } from './common';\n\ntype NestedMenuItemProps = MenuItemProps & {\n label: ReactNode;\n startIcon?: SvgIconComponent;\n endIcon?: SvgIconComponent;\n parentMenuClose: () => void;\n children?: ReactNode;\n items?: MenuItem[];\n};\n\nconst isNodeInstance = (target: EventTarget | null): target is Node => target instanceof Node;\n\nexport const NestedMenuItem: FC<NestedMenuItemProps> = props => {\n const {\n id: providedId,\n label,\n startIcon: StartIconComponent,\n parentMenuClose,\n children,\n items,\n endIcon: _,\n ...menuItemProps\n } = props;\n const [subMenuAnchorEl, setSubMenuAnchorEl] = useState<null | HTMLElement>(null);\n const open = Boolean(subMenuAnchorEl);\n const menuItemRef = useRef<HTMLLIElement>(null);\n const subMenuRef = useRef<HTMLDivElement>(null);\n const generatedId = useId();\n const menuItemId = providedId ?? `nested-menu-trigger-${generatedId}`;\n const subMenuId = `${menuItemId}-submenu`;\n\n const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const clearCloseTimer = useCallback(() => {\n if (closeTimerRef.current) {\n clearTimeout(closeTimerRef.current);\n closeTimerRef.current = null;\n }\n }, []);\n\n const handleClose = useCallback(() => {\n clearCloseTimer();\n setSubMenuAnchorEl(null);\n }, [clearCloseTimer]);\n\n const scheduleClose = useCallback(() => {\n clearCloseTimer();\n closeTimerRef.current = setTimeout(() => {\n handleClose();\n }, CLOSE_DELAY);\n }, [clearCloseTimer, handleClose]);\n\n const handleOpen = (event: MouseEvent<HTMLLIElement> | KeyboardEvent<HTMLLIElement>) => {\n clearCloseTimer();\n setSubMenuAnchorEl(event.currentTarget);\n };\n\n\n // eslint-disable-next-line react-hooks/refs\n const renderChildren = Children.map(children, child => {\n if (!isValidElement(child)) return child;\n\n // Ensure we only process MUI MenuItem children\n if (child.type === MuiMenuItem) {\n const childOnClick = (child.props as MenuItemProps).onClick;\n // Merge any user-defined click logic with the submenu closing behavior.\n const clonedOnClick = (event: MouseEvent<HTMLLIElement>) => {\n childOnClick?.(event);\n handleClose(); // Close the submenu\n parentMenuClose();\n };\n return cloneElement(child, { onClick: clonedOnClick } as Partial<MenuItemProps>);\n }\n return child;\n });\n\n const renderItemsFromData = () => {\n if (!items || items.length === 0) return null;\n\n return items.map((item, index) => {\n if (item.type === 'divider') {\n\n return <Divider key={`divider-${index}`} />;\n }\n\n const {\n type: __,\n items: entryItems,\n startIcon: NestedMenuItemStartIcon,\n endIcon: NestedMenuItemEndIcon,\n label: entryLabel,\n onClick,\n id,\n } = item;\n const entryId = id ?? `${menuItemId}-entry-${index}`;\n const entryKey = `nested-entry-${entryId}`;\n const entryLabelValue = entryLabel ?? entryId;\n\n if (entryItems && entryItems.length > 0) {\n return (\n <NestedMenuItem\n key={entryKey}\n id={entryId}\n label={entryLabelValue}\n startIcon={NestedMenuItemStartIcon}\n endIcon={NestedMenuItemEndIcon}\n parentMenuClose={parentMenuClose}\n items={entryItems}\n />\n );\n }\n\n const handleItemClick = (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n handleClose();\n parentMenuClose();\n };\n\n return (\n <MuiMenuItem key={entryKey} onClick={handleItemClick}>\n <MenuItemContent>\n {NestedMenuItemStartIcon ? <NestedMenuItemStartIcon /> : null}\n <Typography sx={{ flex: 1 }}>{entryLabelValue}</Typography>\n {NestedMenuItemEndIcon ? <NestedMenuItemEndIcon /> : null}\n </MenuItemContent>\n </MuiMenuItem>\n );\n });\n };\n\n const renderedSubMenuItems = items && items.length > 0 ? renderItemsFromData() : renderChildren;\n\n return (\n <>\n <MuiMenuItem\n data-testid={`${menuItemId}-trigger`}\n id={menuItemId}\n ref={menuItemRef}\n onMouseEnter={handleOpen}\n onMouseLeave={e => {\n // CRITICAL FEATURE:\n // Checking whether cursor left the menu item onto the related menu. If so, do not close.\n // TODO(ege): There can be a timeout here before we execute closing to improve UX - in case user is not very precise with mouse.\n if (isNodeInstance(e.relatedTarget) && subMenuRef.current?.contains(e.relatedTarget)) return;\n // If the cursor leaves to anywhere else, close the submenu.\n scheduleClose();\n }}\n onKeyDown={e => {\n e.preventDefault();\n if (e.key === 'ArrowLeft') {\n handleClose();\n }\n if (e.key === 'ArrowRight' || e.key === 'Enter' || e.key === ' ') {\n handleOpen(e);\n }\n }}\n aria-haspopup='menu'\n aria-controls={subMenuId}\n aria-expanded={open ? 'true' : undefined}\n {...menuItemProps}\n >\n <MenuItemContent>\n {StartIconComponent ? <StartIconComponent /> : null}\n <Typography sx={{ flex: 1 }}>{label}</Typography>\n <ArrowRightIcon />\n </MenuItemContent>\n </MuiMenuItem>\n\n <Popper\n data-testid={`${menuItemId}-submenu`}\n open={open}\n ref={subMenuRef}\n anchorEl={subMenuAnchorEl}\n transition\n sx={{ zIndex: t => t.zIndex.modal + 1 }}\n placement='right-start'\n onKeyDown={e => {\n if (e.key === 'ArrowLeft') {\n e.preventDefault();\n handleClose();\n menuItemRef.current?.focus();\n }\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n e.preventDefault();\n e.stopPropagation();\n }\n }}\n onMouseEnter={clearCloseTimer}\n onMouseLeave={e => {\n // CRITICAL FEATURE:\n // Checking whether cursor left the submenu onto the related trigger item. If so, do not close.\n // TODO(ege): There can be a timeout here before we execute closing to improve UX - in case user is not very precise with mouse.\n if (isNodeInstance(e.relatedTarget) && menuItemRef.current?.contains(e.relatedTarget)) return;\n // If the cursor leaves to anywhere else, close the submenu.\n scheduleClose();\n }}\n >\n {({ TransitionProps }) => (\n <Fade {...TransitionProps} timeout={transitionConfig.timeout}>\n <Paper\n elevation={2}\n sx={{\n borderRadius: 1,\n bgcolor: 'background.default',\n }}\n >\n <MenuList\n autoFocusItem\n id={subMenuId}\n aria-labelledby={menuItemId}\n role='menu'\n onKeyDown={e => {\n if (e.key === 'ArrowLeft') {\n const { nativeEvent } = e as KeyboardEvent<HTMLUListElement> & {\n nativeEvent: KeyboardEvent & {\n // our custom flag to avoid duplicate handling in nested menus\n __nestedMenuArrowLeftHandled?: boolean;\n };\n };\n if (nativeEvent.__nestedMenuArrowLeftHandled) {\n // return early if we have already handled this event in a nested menu\n // prevents duplicate logic when the browser bubbles the same keypress through multiple nodes\n return;\n }\n if (!subMenuRef.current?.contains(e.target as Node)) {\n // confirm the event’s target is still inside this submenu;\n // if the keypress originated elsewhere, we don’t continue so other menus can handle it\n return;\n }\n // Mark this event as handled to prevent parent menus from also processing it\n nativeEvent.__nestedMenuArrowLeftHandled = true;\n // preventDefault is needed so the browser doesn’t do its own “arrow left” behavior (like moving the text caret\n // or changing focus) while the menu is managing the navigation. Without this, the closed submenu or focused item\n // might jump unexpectedly even though the menu logic is running.\n e.preventDefault();\n // stopPropagation stops the event from bubbling up to parent DOM nodes that might also have onKeyDown, which could\n // otherwise fight the menu’s own logic or trigger duplicate navigation even though we guard with __nestedMenuArrowLeftHandled\n e.stopPropagation();\n // stopImmediatePropagation keeps any other keydown handlers registered on this same DOM node from running after ours.\n // This is preventing all submenus to close when pressing ArrowLeft in a, say, third level menu. With this, ArrowLeft will only\n // close the current submenu. The __nestedMenuArrowLeftHandled flag only guards against bubbling; this stopImmediatePropagation()\n // ensures no other handlers wired to the same node interfere once we decide to close and focus.\n nativeEvent.stopImmediatePropagation();\n // close the sub menu\n handleClose();\n // move focus back to the parent MenuItem, letting the user continue navigation up the menu hierarchy\n menuItemRef.current?.focus();\n }\n }}\n >\n {renderedSubMenuItems}\n </MenuList>\n </Paper>\n </Fade>\n )}\n </Popper>\n </>\n );\n};\n","'use client';\n\nexport { createSvgIcon as default } from '@mui/material/utils';","\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.js\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"m10 17 5-5-5-5z\"\n}), 'ArrowRight');","import { Stack, styled } from '@mui/material';\nimport Fade from '@mui/material/Fade';\n\nexport const CLOSE_DELAY = 50;\nexport const transitionConfig = {\n type: Fade,\n timeout: { enter: CLOSE_DELAY + 50, exit: CLOSE_DELAY + 50 },\n};\n\nexport const MenuItemContent = styled(Stack)(({ theme }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.spacing(1),\n fontSize: theme.typography.body1.fontSize,\n '& .MuiSvgIcon-root': {\n fontSize: theme.typography.body2.fontSize,\n },\n padding: 0,\n}));\n","import { Menu } from './Menu'\n\nexport type { MenuItem } from './Menu/types'\nexport default Menu\n"],"mappings":"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","names":["Divider","MuiMenuItem","useId","Typography","Fade","Fade","jsx","jsxs","useId","Divider","MuiMenuItem","Typography"]}
|
|
1
|
+
{"version":3,"sources":["../src/Menu/index.tsx","../src/Menu/NestedMenuItem.tsx","../../../node_modules/@mui/icons-material/esm/utils/createSvgIcon.js","../../../node_modules/@mui/icons-material/esm/ArrowRight.js","../src/Menu/common.ts","../src/index.ts"],"sourcesContent":["import type { MenuProps } from '@mui/material/Menu';\nimport MuiMenu from '@mui/material/Menu';\nimport Divider from '@mui/material/Divider';\nimport MuiMenuItem from '@mui/material/MenuItem';\nimport React, { useId } from 'react';\nimport { Typography } from '@mui/material';\nimport { NestedMenuItem } from './NestedMenuItem';\nimport type { MenuItem } from './types';\nimport { DEFAULT_ELEVATION, MenuItemContent, transitionConfig } from './common';\n\nexport type Props = {\n items: MenuItem[];\n onClose?: (event: React.MouseEvent | React.KeyboardEvent, reason: 'itemClick' | 'escapeKeyDown' | 'backdropClick', menuItemId?: string\n ) => void;\n} & Omit<MenuProps, 'onClose'>;\n\nexport function Menu({ items, elevation = DEFAULT_ELEVATION, ...rest }: Props) {\n const menuProps: Omit<Props, 'items'> = { elevation, ...rest }; // setting the elevation for all nested menus here\n const generatedMenuId = useId();\n\n const renderedMenuEntries = items.map((item, index) => {\n if (item.type === 'divider') {\n return <Divider key={`divider-${index}`} />;\n }\n\n const {\n type: _,\n items: nestedItems,\n startIcon: StartIconComponent,\n endIcon: EndIconComponent,\n label,\n onClick,\n id,\n ...menuItemProps\n } = item;\n const entryId = id ?? `${generatedMenuId}-entry-${index}`;\n const itemKey = `menu-item-id:${entryId}`;\n const displayLabel = label ?? entryId;\n\n if (nestedItems && nestedItems.length > 0) {\n return (\n <NestedMenuItem\n key={itemKey}\n id={entryId}\n label={displayLabel}\n startIcon={StartIconComponent}\n endIcon={EndIconComponent}\n parentMenuClose={menuProps.onClose}\n items={nestedItems}\n menuProps={menuProps}\n />\n );\n }\n\n const handleItemClick = (event: React.MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n menuProps.onClose?.(event, \"itemClick\", entryId);\n };\n\n return (\n <MuiMenuItem key={itemKey} onClick={handleItemClick} {...menuItemProps}>\n <MenuItemContent>\n {StartIconComponent ? <StartIconComponent /> : null}\n <Typography sx={{ flex: 1 }}>{displayLabel}</Typography>\n {EndIconComponent ? <EndIconComponent /> : null}\n </MenuItemContent>\n </MuiMenuItem>\n );\n });\n\n return (\n <MuiMenu\n data-testid='root-menu'\n {...menuProps}\n transitionDuration={menuProps.transitionDuration || transitionConfig.timeout}\n slots={{ transition: transitionConfig.type, ...menuProps.slots }}\n >\n {renderedMenuEntries}\n </MuiMenu>\n );\n}\n","import type { FC, ReactNode, MouseEvent, KeyboardEvent } from 'react';\nimport { Children, cloneElement, isValidElement, useCallback, useId, useRef, useState } from 'react';\nimport Fade from '@mui/material/Fade';\nimport type { MenuItemProps } from '@mui/material/MenuItem';\nimport MuiMenuItem from '@mui/material/MenuItem';\nimport Divider from '@mui/material/Divider';\nimport ArrowRightIcon from '@mui/icons-material/ArrowRight';\nimport type { SvgIconComponent } from '@mui/icons-material';\nimport type { MenuListProps, MenuProps, PaperProps } from '@mui/material';\nimport { MenuList, Paper, Popper, Typography } from '@mui/material';\nimport type { MenuItem } from './types';\nimport { CLOSE_DELAY, MenuItemContent, transitionConfig } from './common';\nimport type { Props as BetterMenuProps } from '.';\n\ntype NestedMenuItemProps = MenuItemProps & {\n label: ReactNode;\n startIcon?: SvgIconComponent;\n endIcon?: SvgIconComponent;\n parentMenuClose: BetterMenuProps['onClose'];\n children?: ReactNode;\n items?: MenuItem[];\n menuProps: MenuProps;\n};\n\nconst isNodeInstance = (target: EventTarget | null): target is Node => target instanceof Node;\n\nexport const NestedMenuItem: FC<NestedMenuItemProps> = props => {\n const {\n id: providedId,\n label,\n startIcon: StartIconComponent,\n parentMenuClose,\n children,\n items,\n endIcon: _,\n menuProps,\n ...menuItemProps\n } = props;\n const [subMenuAnchorEl, setSubMenuAnchorEl] = useState<null | HTMLElement>(null);\n const open = Boolean(subMenuAnchorEl);\n const menuItemRef = useRef<HTMLLIElement>(null);\n const subMenuRef = useRef<HTMLDivElement>(null);\n const generatedId = useId();\n const menuItemId = providedId ?? `nested-menu-trigger-${generatedId}`;\n const subMenuId = `${menuItemId}-submenu`;\n\n const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const clearCloseTimer = useCallback(() => {\n if (closeTimerRef.current) {\n clearTimeout(closeTimerRef.current);\n closeTimerRef.current = null;\n }\n }, []);\n\n const handleClose = useCallback(() => {\n clearCloseTimer();\n setSubMenuAnchorEl(null);\n }, [clearCloseTimer]);\n\n const scheduleClose = useCallback(() => {\n clearCloseTimer();\n closeTimerRef.current = setTimeout(() => {\n handleClose();\n }, CLOSE_DELAY);\n }, [clearCloseTimer, handleClose]);\n\n const handleOpen = (event: MouseEvent<HTMLLIElement> | KeyboardEvent<HTMLLIElement>) => {\n clearCloseTimer();\n setSubMenuAnchorEl(event.currentTarget);\n };\n\n\n // eslint-disable-next-line react-hooks/refs\n const renderChildren = Children.map(children, child => {\n if (!isValidElement(child)) return child;\n\n // Ensure we only process MUI MenuItem children\n if (child.type === MuiMenuItem) {\n const childOnClick = (child.props as MenuItemProps).onClick;\n // Merge any user-defined click logic with the submenu closing behavior.\n const clonedOnClick = (event: MouseEvent<HTMLLIElement>) => {\n childOnClick?.(event);\n handleClose(); // Close the submenu\n parentMenuClose?.(event, \"itemClick\", menuItemId);\n };\n return cloneElement(child, { onClick: clonedOnClick } as Partial<MenuItemProps>);\n }\n return child;\n });\n\n const renderItemsFromData = () => {\n if (!items || items.length === 0) return null;\n\n return items.map((item, index) => {\n if (item.type === 'divider') {\n\n return <Divider key={`divider-${index}`} />;\n }\n\n const {\n type: __,\n items: entryItems,\n startIcon: NestedMenuItemStartIcon,\n endIcon: NestedMenuItemEndIcon,\n label: entryLabel,\n onClick,\n id,\n } = item;\n const entryId = id ?? `${menuItemId}-entry-${index}`;\n const entryKey = `nested-entry-${entryId}`;\n const entryLabelValue = entryLabel ?? entryId;\n\n if (entryItems && entryItems.length > 0) {\n return (\n <NestedMenuItem\n key={entryKey}\n id={entryId}\n label={entryLabelValue}\n startIcon={NestedMenuItemStartIcon}\n endIcon={NestedMenuItemEndIcon}\n parentMenuClose={parentMenuClose}\n items={entryItems}\n menuProps={menuProps}\n />\n );\n }\n\n const handleItemClick = (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n handleClose();\n parentMenuClose?.(event, \"itemClick\", entryId);\n };\n\n return (\n <MuiMenuItem key={entryKey} onClick={handleItemClick}>\n <MenuItemContent>\n {NestedMenuItemStartIcon ? <NestedMenuItemStartIcon /> : null}\n <Typography sx={{ flex: 1 }}>{entryLabelValue}</Typography>\n {NestedMenuItemEndIcon ? <NestedMenuItemEndIcon /> : null}\n </MenuItemContent>\n </MuiMenuItem>\n );\n });\n };\n\n const renderedSubMenuItems = items && items.length > 0 ? renderItemsFromData() : renderChildren;\n\n return (\n <>\n <MuiMenuItem\n data-testid={`${menuItemId}-trigger`}\n id={menuItemId}\n ref={menuItemRef}\n onMouseEnter={handleOpen}\n onMouseLeave={e => {\n // CRITICAL FEATURE:\n // Checking whether cursor left the menu item onto the related menu. If so, do not close.\n if (isNodeInstance(e.relatedTarget) && subMenuRef.current?.contains(e.relatedTarget)) return;\n // If the cursor leaves to anywhere else, close the submenu.\n scheduleClose();\n }}\n onKeyDown={e => {\n e.preventDefault();\n if (e.key === 'ArrowLeft') {\n handleClose();\n }\n if (e.key === 'ArrowRight' || e.key === 'Enter' || e.key === ' ') {\n handleOpen(e);\n }\n }}\n aria-haspopup='menu'\n aria-controls={subMenuId}\n aria-expanded={open ? 'true' : undefined}\n {...menuItemProps}\n >\n <MenuItemContent>\n {StartIconComponent ? <StartIconComponent /> : null}\n <Typography sx={{ flex: 1 }}>{label}</Typography>\n <ArrowRightIcon />\n </MenuItemContent>\n </MuiMenuItem>\n\n {/**\n * CRITICAL FEATURE: Menu over Menu fails with focus management and keyboard navigations. \n * It is way more complex to make Menu to work compared to Popper. So, I made the decision to use Popper for submenus.\n * This way we can manage focus and keyboard navigation simpler, and we can also avoid some weird edge cases.\n * If you change this to regular Menu, you will realize that you need to add many custom JS logic to manage focus and keyboard navigation,\n * as well as to block Menu's internal logic.\n */}\n <Popper\n data-testid={`${menuItemId}-submenu`}\n open={open}\n ref={subMenuRef}\n anchorEl={subMenuAnchorEl}\n transition\n sx={{ zIndex: t => t.zIndex.modal + 1 }}\n placement='right-start'\n onKeyDown={e => {\n if (e.key === 'ArrowLeft') {\n e.preventDefault();\n handleClose();\n menuItemRef.current?.focus();\n }\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n e.preventDefault();\n e.stopPropagation();\n }\n }}\n onMouseEnter={clearCloseTimer}\n onMouseLeave={e => {\n // CRITICAL FEATURE:\n // Checking whether cursor left the submenu onto the related trigger item. If so, do not close.\n if (isNodeInstance(e.relatedTarget) && menuItemRef.current?.contains(e.relatedTarget)) return;\n // If the cursor leaves to anywhere else, close the submenu.\n scheduleClose();\n }}\n >\n {({ TransitionProps }) => (\n <Fade {...TransitionProps} timeout={transitionConfig.timeout}>\n <Paper elevation={menuProps.elevation} {...(menuProps?.slotProps?.paper as PaperProps) || {}}>\n <MenuList\n id={subMenuId}\n aria-labelledby={menuItemId}\n role='menu'\n {...(menuProps?.slotProps?.list as MenuListProps) || {}}\n // What's under is not allowed to be overridden for now\n autoFocusItem\n onKeyDown={e => {\n if (e.key === 'ArrowLeft') {\n const { nativeEvent } = e as KeyboardEvent<HTMLUListElement> & {\n nativeEvent: KeyboardEvent & {\n // our custom flag to avoid duplicate handling in nested menus\n __nestedMenuArrowLeftHandled?: boolean;\n };\n };\n if (nativeEvent.__nestedMenuArrowLeftHandled) {\n // return early if we have already handled this event in a nested menu\n // prevents duplicate logic when the browser bubbles the same keypress through multiple nodes\n return;\n }\n if (!subMenuRef.current?.contains(e.target as Node)) {\n // confirm the event’s target is still inside this submenu;\n // if the keypress originated elsewhere, we don’t continue so other menus can handle it\n return;\n }\n // Mark this event as handled to prevent parent menus from also processing it\n nativeEvent.__nestedMenuArrowLeftHandled = true;\n // preventDefault is needed so the browser doesn’t do its own “arrow left” behavior (like moving the text caret\n // or changing focus) while the menu is managing the navigation. Without this, the closed submenu or focused item\n // might jump unexpectedly even though the menu logic is running.\n e.preventDefault();\n // stopPropagation stops the event from bubbling up to parent DOM nodes that might also have onKeyDown, which could\n // otherwise fight the menu’s own logic or trigger duplicate navigation even though we guard with __nestedMenuArrowLeftHandled\n e.stopPropagation();\n // stopImmediatePropagation keeps any other keydown handlers registered on this same DOM node from running after ours.\n // This is preventing all submenus to close when pressing ArrowLeft in a, say, third level menu. With this, ArrowLeft will only\n // close the current submenu. The __nestedMenuArrowLeftHandled flag only guards against bubbling; this stopImmediatePropagation()\n // ensures no other handlers wired to the same node interfere once we decide to close and focus.\n nativeEvent.stopImmediatePropagation();\n // close the sub menu\n handleClose();\n // move focus back to the parent MenuItem, letting the user continue navigation up the menu hierarchy\n menuItemRef.current?.focus();\n }\n }}\n >\n {renderedSubMenuItems}\n </MenuList>\n </Paper>\n </Fade>\n )}\n </Popper>\n </>\n );\n};\n","'use client';\n\nexport { createSvgIcon as default } from '@mui/material/utils';","\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.js\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"m10 17 5-5-5-5z\"\n}), 'ArrowRight');","import { Stack, styled } from '@mui/material';\nimport Fade from '@mui/material/Fade';\n\nexport const CLOSE_DELAY = 50;\nexport const transitionConfig = {\n type: Fade,\n timeout: { enter: CLOSE_DELAY + 50, exit: CLOSE_DELAY + 50 },\n};\nexport const DEFAULT_ELEVATION = 8; // this is also what MUI menu uses as default\n\nexport const MenuItemContent = styled(Stack)({\n flexDirection: 'row',\n alignItems: 'center',\n gap: '8px',\n padding: 0,\n});\n","import { Menu } from './Menu'\n\nexport type { MenuItem } from './Menu/types'\nexport default Menu\n"],"mappings":"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","names":["Divider","MuiMenuItem","useId","Typography","Fade","Fade","jsx","jsxs","useId","Divider","MuiMenuItem","Typography"]}
|