mui-nested-menu
Version:
Infinitely deep nested menu items for MUI 5.
372 lines (311 loc) • 15 kB
JavaScript
var $hXhXL$reactjsxruntime = require("react/jsx-runtime");
var $hXhXL$muimaterialMenu = require("@mui/material/Menu");
var $hXhXL$react = require("react");
var $hXhXL$muimaterialMenuItem = require("@mui/material/MenuItem");
var $hXhXL$muimaterialstyles = require("@mui/material/styles");
var $hXhXL$muimaterialTypography = require("@mui/material/Typography");
var $hXhXL$muisystemBox = require("@mui/system/Box");
var $hXhXL$muimaterialSvgIcon = require("@mui/material/SvgIcon");
var $hXhXL$muimaterialButton = require("@mui/material/Button");
function $parcel$exportWildcard(dest, source) {
Object.keys(source).forEach(function(key) {
if (key === 'default' || key === '__esModule' || Object.prototype.hasOwnProperty.call(dest, key)) {
return;
}
Object.defineProperty(dest, key, {
enumerable: true,
get: function get() {
return source[key];
}
});
});
return dest;
}
function $parcel$interopDefault(a) {
return a && a.__esModule ? a.default : a;
}
function $parcel$export(e, n, v, s) {
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
}
var $681a5bb3d531a14d$exports = {};
var $18e895906fc59095$exports = {};
$parcel$export($18e895906fc59095$exports, "ContextMenu", () => $18e895906fc59095$export$8dc6765e8be191c7);
var $f6841acb38ae6244$exports = {};
$parcel$export($f6841acb38ae6244$exports, "nestedMenuItemsFromObject", () => $f6841acb38ae6244$export$fa07f0a23a9d3fdc);
var $4bfc6f7ac91dd60d$exports = {};
$parcel$export($4bfc6f7ac91dd60d$exports, "IconMenuItem", () => $4bfc6f7ac91dd60d$export$29aa74ef483b19fe);
const $4bfc6f7ac91dd60d$var$StyledMenuItem = (0, $hXhXL$muimaterialstyles.styled)((0, ($parcel$interopDefault($hXhXL$muimaterialMenuItem))))({
display: 'flex',
justifyContent: 'space-between',
paddingLeft: '4px',
paddingRight: '4px'
});
const $4bfc6f7ac91dd60d$var$StyledTypography = (0, $hXhXL$muimaterialstyles.styled)((0, ($parcel$interopDefault($hXhXL$muimaterialTypography))))({
paddingLeft: '8px',
paddingRight: '8px',
textAlign: 'left'
});
const $4bfc6f7ac91dd60d$var$FlexBox = (0, $hXhXL$muimaterialstyles.styled)((0, ($parcel$interopDefault($hXhXL$muisystemBox))))({
display: 'flex'
});
const $4bfc6f7ac91dd60d$export$29aa74ef483b19fe = /*#__PURE__*/ (0, $hXhXL$react.forwardRef)(function IconMenuItem({ MenuItemProps: MenuItemProps, className: className, label: label, leftIcon: leftIcon, renderLabel: renderLabel, rightIcon: rightIcon, ...props }, ref) {
return /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsxs)($4bfc6f7ac91dd60d$var$StyledMenuItem, {
...MenuItemProps,
ref: ref,
className: className,
...props,
children: [
/*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsxs)($4bfc6f7ac91dd60d$var$FlexBox, {
children: [
leftIcon,
renderLabel ? renderLabel() : /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)($4bfc6f7ac91dd60d$var$StyledTypography, {
children: label
})
]
}),
rightIcon
]
});
});
var $f5195a18afaf3fe7$exports = {};
$parcel$export($f5195a18afaf3fe7$exports, "NestedMenuItem", () => $f5195a18afaf3fe7$export$a49133e888f99d9b);
const $e308e62c69875369$export$6172d85aadfc9b96 = (props)=>{
return /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, ($parcel$interopDefault($hXhXL$muimaterialSvgIcon))), {
...props,
children: /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)("path", {
d: "M9.29 6.71c-.39.39-.39 1.02 0 1.41L13.17 12l-3.88 3.88c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0l4.59-4.59c.39-.39.39-1.02 0-1.41L10.7 6.7c-.38-.38-1.02-.38-1.41.01z"
})
});
};
const $f5195a18afaf3fe7$export$a49133e888f99d9b = /*#__PURE__*/ (0, $hXhXL$react.forwardRef)(function NestedMenuItem(props, ref) {
const { parentMenuOpen: parentMenuOpen, label: label, renderLabel: renderLabel, rightIcon: rightIcon = /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, $e308e62c69875369$export$6172d85aadfc9b96), {}), leftIcon: leftIcon = null, children: children, className: className, tabIndex: tabIndexProp, ContainerProps: ContainerPropsProp = {}, MenuProps: MenuProps, delay: delay = 0, ...MenuItemProps } = props;
const { ref: containerRefProp, ...ContainerProps } = ContainerPropsProp;
const menuItemRef = (0, $hXhXL$react.useRef)(null);
(0, $hXhXL$react.useImperativeHandle)(ref, ()=>menuItemRef.current); // eslint-disable-line @typescript-eslint/no-non-null-assertion
const containerRef = (0, $hXhXL$react.useRef)(null);
(0, $hXhXL$react.useImperativeHandle)(containerRefProp, ()=>containerRef.current);
const menuContainerRef = (0, $hXhXL$react.useRef)(null);
const timeoutRef = (0, $hXhXL$react.useRef)(null);
const [isSubMenuOpen, setIsSubMenuOpen] = (0, $hXhXL$react.useState)(false);
const handleMouseEnter = (e)=>{
timeoutRef.current = setTimeout(()=>{
if (!props.disabled) setIsSubMenuOpen(true);
if (ContainerProps.onMouseEnter) ContainerProps.onMouseEnter(e);
}, delay);
};
const handleMouseLeave = (e)=>{
timeoutRef.current && clearTimeout(timeoutRef.current);
setIsSubMenuOpen(false);
if (ContainerProps.onMouseLeave) ContainerProps.onMouseLeave(e);
};
// Check if any immediate children are active
const isSubmenuFocused = ()=>{
const active = containerRef.current?.ownerDocument.activeElement ?? null;
if (menuContainerRef.current == null) return false;
for (const child of menuContainerRef.current.children){
if (child === active) return true;
}
return false;
};
const handleFocus = (e)=>{
if (e.target === containerRef.current && !props.disabled) setIsSubMenuOpen(true);
if (ContainerProps.onFocus) ContainerProps.onFocus(e);
};
const handleKeyDown = (e)=>{
if (e.key === 'Escape') return;
if (isSubmenuFocused()) e.stopPropagation();
const active = containerRef.current?.ownerDocument.activeElement;
if (e.key === 'ArrowLeft' && isSubmenuFocused()) containerRef.current?.focus();
if (e.key === 'ArrowRight' && e.target === containerRef.current && e.target === active) {
const firstChild = menuContainerRef.current?.children[0];
firstChild?.focus();
}
};
const open = isSubMenuOpen && parentMenuOpen;
// Root element must have a `tabIndex` attribute for keyboard navigation
let tabIndex;
if (!props.disabled) tabIndex = tabIndexProp !== undefined ? tabIndexProp : -1;
return /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsxs)("div", {
...ContainerProps,
ref: containerRef,
onFocus: handleFocus,
tabIndex: tabIndex,
onMouseEnter: handleMouseEnter,
onMouseLeave: handleMouseLeave,
onKeyDown: handleKeyDown,
children: [
/*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, $4bfc6f7ac91dd60d$export$29aa74ef483b19fe), {
MenuItemProps: MenuItemProps,
className: className,
ref: menuItemRef,
leftIcon: leftIcon,
rightIcon: rightIcon,
label: label,
renderLabel: renderLabel
}),
/*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, ($parcel$interopDefault($hXhXL$muimaterialMenu))), {
// Set pointer events to 'none' to prevent the invisible Popover div
// from capturing events for clicks and hovers
style: {
pointerEvents: 'none'
},
anchorEl: menuItemRef.current,
anchorOrigin: {
horizontal: 'right',
vertical: 'top'
},
transformOrigin: {
horizontal: 'left',
vertical: 'top'
},
open: open,
autoFocus: false,
disableAutoFocus: true,
disableEnforceFocus: true,
onClose: ()=>{
setIsSubMenuOpen(false);
},
...MenuProps,
children: /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)("div", {
ref: menuContainerRef,
style: {
pointerEvents: 'auto'
},
children: children
})
})
]
});
});
$f5195a18afaf3fe7$export$a49133e888f99d9b.displayName = 'NestedMenuItem';
function $f6841acb38ae6244$export$fa07f0a23a9d3fdc({ menuItemsData: items, isOpen: isOpen, handleClose: handleClose }) {
return items.map((item)=>{
const { leftIcon: leftIcon, rightIcon: rightIcon, label: label, items: items, callback: callback, sx: sx, disabled: disabled, delay: delay } = item;
if (items && items.length > 0) // Recurse deeper
return /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, $f5195a18afaf3fe7$export$a49133e888f99d9b), {
leftIcon: leftIcon,
rightIcon: rightIcon,
label: label,
parentMenuOpen: isOpen,
sx: sx,
delay: delay,
disabled: disabled,
children: $f6841acb38ae6244$export$fa07f0a23a9d3fdc({
handleClose: handleClose,
isOpen: isOpen,
menuItemsData: items
})
}, label);
else // No children elements, return MenuItem
return /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, $4bfc6f7ac91dd60d$export$29aa74ef483b19fe), {
leftIcon: leftIcon,
rightIcon: rightIcon,
label: label,
onClick: (event)=>{
handleClose();
callback && callback(event, item);
},
sx: sx,
disabled: disabled
}, label);
});
}
const $18e895906fc59095$export$8dc6765e8be191c7 = /*#__PURE__*/ (0, $hXhXL$react.forwardRef)(function ContextMenu({ children: children, menuItems: menuItems, menuItemsData: menuItemsData }, ref) {
const wrapperRef = ref ?? (0, $hXhXL$react.useRef)(null);
const [menuPosition, setMenuPosition] = (0, $hXhXL$react.useState)(null);
const [mouseDownPosition, setMouseDownPosition] = (0, $hXhXL$react.useState)(null);
const handleItemClick = ()=>setMenuPosition(null);
const handleMouseDown = (e)=>{
if (menuPosition !== null) setMenuPosition(null);
if (e.button !== 2) return;
const wrapperBounds = wrapperRef.current.getBoundingClientRect();
if (e.clientX < wrapperBounds.left || e.clientX > wrapperBounds.right || e.clientY < wrapperBounds.top || e.clientY > wrapperBounds.bottom) return;
setMouseDownPosition({
left: e.clientX,
top: e.clientY
});
};
const handleMouseUp = (e)=>{
const top = e.clientY;
const left = e.clientX;
if (mouseDownPosition === null) return;
if (mouseDownPosition.top === top && mouseDownPosition.left === left) setMenuPosition({
left: e.clientX,
top: e.clientY
});
};
const menuContents = menuItems ?? (menuItemsData && (0, $f6841acb38ae6244$export$fa07f0a23a9d3fdc)({
handleClose: handleItemClick,
isOpen: !!menuPosition,
menuItemsData: menuItemsData
}));
return /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsxs)("div", {
ref: wrapperRef,
onContextMenu: (e)=>e.preventDefault(),
onMouseDown: handleMouseDown,
onMouseUp: handleMouseUp,
children: [
menuPosition && /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, ($parcel$interopDefault($hXhXL$muimaterialMenu))), {
onContextMenu: (e)=>e.preventDefault(),
open: !!menuPosition,
onClose: ()=>setMenuPosition(null),
anchorReference: "anchorPosition",
anchorPosition: menuPosition,
children: menuContents
}),
children
]
});
});
var $70717eed00f20fb6$exports = {};
$parcel$export($70717eed00f20fb6$exports, "NestedDropdown", () => $70717eed00f20fb6$export$84d8e229d5f1c7fc);
const $83896c6160d9245a$export$4e3778eb35f54199 = (props)=>{
return /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, ($parcel$interopDefault($hXhXL$muimaterialSvgIcon))), {
...props,
children: /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)("path", {
d: "M8.12 9.29 12 13.17l3.88-3.88c.39-.39 1.02-.39 1.41 0 .39.39.39 1.02 0 1.41l-4.59 4.59c-.39.39-1.02.39-1.41 0L6.7 10.7a.9959.9959 0 0 1 0-1.41c.39-.38 1.03-.39 1.42 0z"
})
});
};
const $70717eed00f20fb6$export$84d8e229d5f1c7fc = /*#__PURE__*/ (0, $hXhXL$react.forwardRef)(function NestedDropdown(props, ref) {
const [anchorEl, setAnchorEl] = (0, $hXhXL$react.useState)(null);
const open = Boolean(anchorEl);
const { menuItemsData: data, onClick: onClick, ButtonProps: ButtonProps, MenuProps: MenuProps, ...rest } = props;
const handleClick = (e)=>{
setAnchorEl(e.currentTarget);
onClick && onClick(e);
};
const handleClose = ()=>setAnchorEl(null);
const menuItems = (0, $f6841acb38ae6244$export$fa07f0a23a9d3fdc)({
handleClose: handleClose,
isOpen: open,
menuItemsData: data?.items ?? []
});
return /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsxs)("div", {
ref: ref,
...rest,
children: [
/*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, ($parcel$interopDefault($hXhXL$muimaterialButton))), {
onClick: handleClick,
endIcon: /*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, $83896c6160d9245a$export$4e3778eb35f54199), {}),
...ButtonProps,
children: data?.label ?? 'Menu'
}),
/*#__PURE__*/ (0, $hXhXL$reactjsxruntime.jsx)((0, ($parcel$interopDefault($hXhXL$muimaterialMenu))), {
anchorEl: anchorEl,
open: open,
onClose: handleClose,
...MenuProps,
children: menuItems
})
]
});
});
$parcel$exportWildcard($681a5bb3d531a14d$exports, $18e895906fc59095$exports);
$parcel$exportWildcard($681a5bb3d531a14d$exports, $4bfc6f7ac91dd60d$exports);
$parcel$exportWildcard($681a5bb3d531a14d$exports, $70717eed00f20fb6$exports);
$parcel$exportWildcard($681a5bb3d531a14d$exports, $f5195a18afaf3fe7$exports);
$parcel$exportWildcard($681a5bb3d531a14d$exports, $f6841acb38ae6244$exports);
var $dbc53ae45b7d68af$exports = {};
$parcel$exportWildcard(module.exports, $681a5bb3d531a14d$exports);
$parcel$exportWildcard(module.exports, $dbc53ae45b7d68af$exports);