parent
5b231fdb7e
commit
753358a13b
2 changed files with 89 additions and 0 deletions
@ -0,0 +1,83 @@ |
||||
import { MoreVert as MoreVertIcon } from '@mui/icons-material'; |
||||
import { Box } from '@mui/material'; |
||||
import { FC, MouseEventHandler, useCallback, useMemo, useState } from 'react'; |
||||
|
||||
import ContainedButton from './ContainedButton'; |
||||
import IconButton from './IconButton/IconButton'; |
||||
import Menu from './Menu'; |
||||
|
||||
const ButtonWithMenu: FC<ButtonWithMenuProps> = (props) => { |
||||
const { |
||||
children, |
||||
muiMenuProps, |
||||
onButtonClick, |
||||
onItemClick, |
||||
variant = 'icon', |
||||
...menuProps |
||||
} = props; |
||||
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null); |
||||
|
||||
const open = useMemo(() => Boolean(anchorEl), [anchorEl]); |
||||
|
||||
const buttonContent = useMemo(() => children ?? <MoreVertIcon />, [children]); |
||||
|
||||
const buttonClickHandler = useCallback<MouseEventHandler<HTMLButtonElement>>( |
||||
(...args) => { |
||||
const { |
||||
0: { currentTarget }, |
||||
} = args; |
||||
|
||||
setAnchorEl(currentTarget); |
||||
|
||||
return onButtonClick?.call(null, ...args); |
||||
}, |
||||
[onButtonClick], |
||||
); |
||||
|
||||
const buttonElement = useMemo(() => { |
||||
if (variant === 'contained') { |
||||
return ( |
||||
<ContainedButton onClick={buttonClickHandler}> |
||||
{buttonContent} |
||||
</ContainedButton> |
||||
); |
||||
} |
||||
|
||||
return ( |
||||
<IconButton onClick={buttonClickHandler}>{buttonContent}</IconButton> |
||||
); |
||||
}, [buttonClickHandler, buttonContent, variant]); |
||||
|
||||
const itemClickHandler = useCallback< |
||||
Exclude<MenuProps['onItemClick'], undefined> |
||||
>( |
||||
(key, value, ...rest) => { |
||||
setAnchorEl(null); |
||||
|
||||
return onItemClick?.call(null, key, value, ...rest); |
||||
}, |
||||
[onItemClick], |
||||
); |
||||
|
||||
return ( |
||||
<Box> |
||||
{buttonElement} |
||||
<Menu |
||||
muiMenuProps={{ |
||||
anchorEl, |
||||
keepMounted: true, |
||||
onClose: () => setAnchorEl(null), |
||||
...muiMenuProps, |
||||
}} |
||||
onItemClick={itemClickHandler} |
||||
open={open} |
||||
{...menuProps} |
||||
/> |
||||
</Box> |
||||
); |
||||
}; |
||||
|
||||
export default ButtonWithMenu as <T>( |
||||
props: ButtonWithMenuProps<T>, |
||||
) => ReturnType<FC<ButtonWithMenuProps<T>>>; |
@ -0,0 +1,6 @@ |
||||
type ButtonWithMenuOptionalProps<T = unknown> = Omit<MenuProps<T>, 'open'> & { |
||||
onButtonClick?: import('react').MouseEventHandler<HTMLButtonElement>; |
||||
variant?: 'contained' | 'icon'; |
||||
}; |
||||
|
||||
type ButtonWithMenuProps<T = unknown> = ButtonWithMenuOptionalProps<T>; |
Loading…
Reference in new issue