21720: fixed menu item spacing
[arvados.git] / services / workbench2 / src / components / multiselect-toolbar / ms-toolbar-overflow-menu.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React, { useState, useMemo, ReactElement, JSXElementConstructor } from 'react';
6 import { DoubleRightArrows } from 'components/icon/icon';
7 import classnames from 'classnames';
8 import { CustomStyleRulesCallback } from 'common/custom-theme';
9 import { IconButton, Menu, MenuItem, Tooltip } from '@mui/material';
10 import { WithStyles } from '@mui/styles';
11 import withStyles from '@mui/styles/withStyles';
12 import { ArvadosTheme } from 'common/custom-theme';
13
14 type CssRules = 'inOverflowMenu' | 'openMenuButton' | 'menu' | 'menuItem' | 'menuElement';
15
16 const styles: CustomStyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
17     inOverflowMenu: {
18         '&:hover': {
19             backgroundColor: 'transparent',
20         },
21     },
22     openMenuButton: {
23         right: '10px',
24     },
25     menu: {
26         marginLeft: 0,
27     },
28     menuItem: {
29         '&:hover': {
30             backgroundColor: 'white',
31         },
32         marginTop: 0,
33         paddingTop: 0,
34         paddingLeft: '1rem',
35         height: '2.5rem',
36     },
37     menuElement: {
38         width: '2rem',
39     }
40 });
41
42 export type OverflowChild = ReactElement<{ className: string; }, string | JSXElementConstructor<any>>
43
44 type OverflowMenuProps = {
45     children: OverflowChild[]
46     className: string
47     visibilityMap: {}
48 }
49
50 export const OverflowMenu = withStyles(styles)((props: OverflowMenuProps & WithStyles<CssRules>) => {
51     const { children, className, visibilityMap, classes } = props;
52     const [anchorEl, setAnchorEl] = useState(null);
53     const open = Boolean(anchorEl);
54     const handleClick = (event) => {
55         setAnchorEl(event.currentTarget);
56     };
57
58     const handleClose = () => {
59         setAnchorEl(null);
60     };
61
62     const shouldShowMenu = useMemo(() => Object.values(visibilityMap).some((v) => v === false), [visibilityMap]);
63     if (!shouldShowMenu) {
64         return null;
65     }
66     return (
67         <div className={className}>
68             <Tooltip title="More options" disableFocusListener>
69                 <IconButton
70                     aria-label='more'
71                     aria-controls='long-menu'
72                     aria-haspopup='true'
73                     onClick={handleClick}
74                     className={classes.openMenuButton}
75                     data-cy='overflow-menu-button'
76                     size="large">
77                         <DoubleRightArrows />
78                 </IconButton>
79             </Tooltip>
80             <Menu
81                 id='long-menu'
82                 anchorEl={anchorEl}
83                 keepMounted
84                 open={open}
85                 onClose={handleClose}
86                 disableAutoFocusItem
87                 className={classes.menu}
88                 data-cy='overflow-menu'
89             >
90                 {React.Children.map(children, (child: any) => {
91                     if (!visibilityMap[child.props['data-targetid']]) {
92                         return <MenuItem
93                                 key={child}
94                                 onClick={handleClose}
95                                 className={classes.menuItem}
96                             >
97                                 {React.cloneElement(child, {
98                                     className: classnames(classes.menuElement),
99                                 })}
100                             </MenuItem>
101                     }
102                     return null;
103                 })}
104             </Menu>
105         </div>
106     );
107 });