@jbrowse/core
Version:
JBrowse 2 core libraries used by plugins
234 lines (233 loc) • 11.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MenuItemEndDecoration = MenuItemEndDecoration;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const ArrowRight_1 = __importDefault(require("@mui/icons-material/ArrowRight"));
const CheckBox_1 = __importDefault(require("@mui/icons-material/CheckBox"));
const CheckBoxOutlineBlank_1 = __importDefault(require("@mui/icons-material/CheckBoxOutlineBlank"));
const RadioButtonChecked_1 = __importDefault(require("@mui/icons-material/RadioButtonChecked"));
const RadioButtonUnchecked_1 = __importDefault(require("@mui/icons-material/RadioButtonUnchecked"));
const material_1 = require("@mui/material");
const mui_1 = require("tss-react/mui");
const util_1 = require("../util");
const useStyles = (0, mui_1.makeStyles)()({
paper: {
position: 'fixed',
overflowY: 'auto',
overflowX: 'hidden',
minWidth: 16,
minHeight: 16,
maxWidth: 'calc(100% - 32px)',
maxHeight: 'calc(100% - 32px)',
top: 0,
left: 0,
outline: 0,
},
menuItemEndDecoration: {
padding: 0,
margin: 0,
height: 16,
},
});
function MenuItemEndDecoration(props) {
const { classes } = useStyles();
const { type } = props;
let checked;
let disabled;
if ('checked' in props) {
;
({ checked, disabled } = props);
}
let icon;
switch (type) {
case 'subMenu': {
icon = (0, jsx_runtime_1.jsx)(ArrowRight_1.default, { color: "action" });
break;
}
case 'checkbox': {
if (checked) {
const color = disabled ? 'inherit' : undefined;
icon = (0, jsx_runtime_1.jsx)(CheckBox_1.default, { color: color });
}
else {
icon = (0, jsx_runtime_1.jsx)(CheckBoxOutlineBlank_1.default, { color: "action" });
}
break;
}
case 'radio': {
if (checked) {
const color = disabled ? 'inherit' : undefined;
icon = (0, jsx_runtime_1.jsx)(RadioButtonChecked_1.default, { color: color });
}
else {
icon = (0, jsx_runtime_1.jsx)(RadioButtonUnchecked_1.default, { color: "action" });
}
break;
}
}
return (0, jsx_runtime_1.jsx)("div", { className: classes.menuItemEndDecoration, children: icon });
}
function checkIfValid(m) {
return m.type !== 'divider' && m.type !== 'subHeader' && !m.disabled;
}
function findNextValidIdx(menuItems, currentIdx) {
const idx = menuItems.slice(currentIdx + 1).findIndex(checkIfValid);
if (idx === -1) {
return idx;
}
return currentIdx + 1 + idx;
}
function findPreviousValidIdx(menuItems, currentIdx) {
return (0, util_1.findLastIndex)(menuItems.slice(0, currentIdx), checkIfValid);
}
const MenuPage = (0, react_1.forwardRef)(function MenuPage2(props, ref) {
const [subMenuAnchorEl, setSubMenuAnchorEl] = (0, react_1.useState)();
const [openSubMenuIdx, setOpenSubMenuIdx] = (0, react_1.useState)();
const [isSubMenuOpen, setIsSubMenuOpen] = (0, react_1.useState)(false);
const [selectedMenuItemIdx, setSelectedMenuItemIdx] = (0, react_1.useState)();
const [position, setPosition] = (0, react_1.useState)();
const paperRef = (0, react_1.useRef)(null);
const { classes } = useStyles();
const { menuItems, onMenuItemClick, open, onClose, anchorEl, top = false, } = props;
(0, react_1.useEffect)(() => {
if (!open) {
setSubMenuAnchorEl(undefined);
setOpenSubMenuIdx(undefined);
}
}, [open]);
(0, react_1.useEffect)(() => {
const shouldSubMenuBeOpen = open && Boolean(subMenuAnchorEl);
let timer;
if (shouldSubMenuBeOpen && !isSubMenuOpen) {
timer = setTimeout(() => {
setIsSubMenuOpen(true);
}, 300);
}
else if (!shouldSubMenuBeOpen && isSubMenuOpen) {
timer = setTimeout(() => {
setIsSubMenuOpen(false);
}, 300);
}
return () => {
clearTimeout(timer);
};
}, [isSubMenuOpen, open, subMenuAnchorEl]);
(0, react_1.useEffect)(() => {
if (anchorEl) {
const rect = anchorEl.getBoundingClientRect();
if (position) {
if (rect.top !== position.top ||
rect.left + rect.width !== position.left) {
setPosition({ top: rect.top, left: rect.left + rect.width });
}
}
else {
setPosition({ top: rect.top, left: rect.left + rect.width });
}
}
else if (!position) {
setPosition({});
}
}, [position, anchorEl]);
const hasIcon = menuItems.some(menuItem => 'icon' in menuItem && menuItem.icon);
const menuItemStyle = {};
function handleClick(callback) {
return (event) => {
onMenuItemClick(event, callback);
};
}
const ListContents = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(material_1.MenuList, { autoFocusItem: open && !isSubMenuOpen, dense: true, children: menuItems
.sort((a, b) => (b.priority || 0) - (a.priority || 0))
.map((menuItem, idx) => {
if (menuItem.type === 'divider') {
return ((0, jsx_runtime_1.jsx)(material_1.Divider, { component: "li" }, `divider-${JSON.stringify(menuItem)}-${idx}`));
}
if (menuItem.type === 'subHeader') {
return ((0, jsx_runtime_1.jsx)(material_1.ListSubheader, { children: menuItem.label }, `subHeader-${menuItem.label}-${idx}`));
}
let icon = null;
let endDecoration = null;
if (menuItem.icon) {
const Icon = menuItem.icon;
icon = ((0, jsx_runtime_1.jsx)(material_1.ListItemIcon, { children: (0, jsx_runtime_1.jsx)(Icon, {}) }));
}
if ('subMenu' in menuItem) {
endDecoration = (0, jsx_runtime_1.jsx)(MenuItemEndDecoration, { type: "subMenu" });
}
else if (menuItem.type === 'checkbox' ||
menuItem.type === 'radio') {
endDecoration = ((0, jsx_runtime_1.jsx)(MenuItemEndDecoration, { type: menuItem.type, checked: menuItem.checked, disabled: menuItem.disabled }));
}
const onClick = 'onClick' in menuItem
? handleClick(menuItem.onClick)
: undefined;
return ((0, jsx_runtime_1.jsxs)(material_1.MenuItem, { style: menuItemStyle, selected: idx === selectedMenuItemIdx, onClick: onClick, onMouseMove: e => {
if (e.currentTarget !== document.activeElement) {
e.currentTarget.focus();
setSelectedMenuItemIdx(idx);
}
if ('subMenu' in menuItem) {
if (openSubMenuIdx !== idx) {
setSubMenuAnchorEl(e.currentTarget);
setOpenSubMenuIdx(idx);
}
}
else {
setSubMenuAnchorEl(undefined);
setOpenSubMenuIdx(undefined);
}
}, onKeyDown: e => {
switch (e.key) {
case 'ArrowLeft':
case 'Escape': {
onClose === null || onClose === void 0 ? void 0 : onClose(e, 'escapeKeyDown');
break;
}
case 'ArrowUp': {
setSelectedMenuItemIdx(findPreviousValidIdx(menuItems, idx));
break;
}
case 'ArrowDown': {
const a = findNextValidIdx(menuItems, idx);
setSelectedMenuItemIdx(a);
break;
}
default: {
if ('subMenu' in menuItem &&
(e.key === 'ArrowRight' || e.key === 'Enter')) {
setSubMenuAnchorEl(e.currentTarget);
setOpenSubMenuIdx(idx);
setIsSubMenuOpen(true);
}
}
}
}, disabled: Boolean(menuItem.disabled), children: [icon, (0, jsx_runtime_1.jsx)(material_1.ListItemText, { primary: menuItem.label, secondary: menuItem.subLabel, inset: hasIcon && !menuItem.icon }), endDecoration] }, menuItem.id || String(menuItem.label)));
}) }), menuItems.map((menuItem, idx) => {
let subMenu = null;
if ('subMenu' in menuItem) {
subMenu = ((0, jsx_runtime_1.jsx)(MenuPage, { anchorEl: subMenuAnchorEl, open: isSubMenuOpen && openSubMenuIdx === idx, onClose: () => {
setIsSubMenuOpen(false);
setSubMenuAnchorEl(undefined);
}, onMenuItemClick: onMenuItemClick, menuItems: menuItem.subMenu }, menuItem.id || String(menuItem.label)));
}
return subMenu;
})] }));
return top ? (ListContents) : ((0, jsx_runtime_1.jsx)(material_1.Grow, { in: open, style: { transformOrigin: '0 0 0' }, ref: ref, children: (0, jsx_runtime_1.jsx)(material_1.Paper, { elevation: 8, ref: paperRef, className: classes.paper, style: { ...position }, children: ListContents }) }));
});
function Menu(props) {
const { open, onClose, menuItems, onMenuItemClick, ...other } = props;
return ((0, jsx_runtime_1.jsx)(material_1.Popover, { open: open, onClose: onClose, anchorOrigin: {
vertical: 'bottom',
horizontal: 'right',
...other.anchorOrigin,
}, transformOrigin: {
vertical: 'top',
horizontal: 'left',
...other.transformOrigin,
}, ...other, children: (0, jsx_runtime_1.jsx)(MenuPage, { open: open, onClose: onClose, menuItems: menuItems, onMenuItemClick: onMenuItemClick, top: true }) }));
}
exports.default = Menu;