Add secondary features to the calling interface UI
GitLab: #58
Change-Id: I86c813e63f74e5a36b92a2f4af8ca61a72381040
diff --git a/client/src/components/Button.tsx b/client/src/components/Button.tsx
index c33def2..4937c0b 100644
--- a/client/src/components/Button.tsx
+++ b/client/src/components/Button.tsx
@@ -16,10 +16,23 @@
* <https://www.gnu.org/licenses/>.
*/
import { QuestionMark, RadioButtonChecked, RadioButtonUnchecked } from '@mui/icons-material';
-import { Box, ClickAwayListener, IconButton, IconButtonProps, Popper, SvgIconProps } from '@mui/material';
+import {
+ Box,
+ ClickAwayListener,
+ IconButton,
+ IconButtonProps,
+ ListItemIcon,
+ ListItemText,
+ Menu,
+ MenuItem,
+ Popper,
+ Radio,
+ RadioGroup,
+ SvgIconProps,
+} from '@mui/material';
import { styled } from '@mui/material/styles';
import EmojiPicker, { IEmojiData } from 'emoji-picker-react';
-import { ComponentType, MouseEvent, useCallback, useState } from 'react';
+import React, { ComponentType, MouseEvent, ReactNode, useCallback, useState } from 'react';
import {
Arrow2Icon,
@@ -32,6 +45,7 @@
CrossedEyeIcon,
CrossIcon,
EmojiIcon,
+ ExpandLessIcon,
EyeIcon,
FolderIcon,
InfoIcon,
@@ -78,6 +92,101 @@
},
}));
+type ExpandMenuOption = {
+ description: ReactNode;
+ icon?: ReactNode;
+};
+
+type ExpandMenuRadioOption = {
+ options: {
+ key: string;
+ description: ReactNode;
+ }[];
+ defaultSelectedOption?: string;
+};
+
+export type ExpandableButtonProps = IconButtonProps & {
+ Icon?: ComponentType<SvgIconProps>;
+ expandMenuOptions?: (ExpandMenuOption | ExpandMenuRadioOption)[];
+};
+
+export const ExpandableButton = styled(({ Icon, expandMenuOptions = [], ...props }: ExpandableButtonProps) => {
+ const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
+ const handleClose = () => {
+ setAnchorEl(null);
+ };
+
+ const hasExpandMenuOptions = expandMenuOptions.length > 0;
+ return (
+ <Box>
+ <Menu
+ anchorEl={anchorEl}
+ open={!!anchorEl}
+ onClose={handleClose}
+ anchorOrigin={{
+ vertical: 'top',
+ horizontal: 'center',
+ }}
+ transformOrigin={{
+ vertical: 'bottom',
+ horizontal: 'center',
+ }}
+ >
+ {expandMenuOptions.map((option, id) => {
+ if ('options' in option) {
+ const { options, defaultSelectedOption } = option;
+ return (
+ <RadioGroup key={id} defaultValue={defaultSelectedOption}>
+ {options.map(({ description, key }) => {
+ return (
+ <MenuItem key={key}>
+ <ListItemIcon>
+ <Radio value={key} />
+ </ListItemIcon>
+ <ListItemText>{description}</ListItemText>
+ </MenuItem>
+ );
+ })}
+ </RadioGroup>
+ );
+ }
+
+ return (
+ <MenuItem key={id} onClick={handleClose}>
+ <ListItemIcon>{option.icon}</ListItemIcon>
+ <ListItemText>{option.description}</ListItemText>
+ </MenuItem>
+ );
+ })}
+ </Menu>
+ <Box position="relative">
+ {hasExpandMenuOptions && (
+ <IconButton
+ {...props}
+ onClick={(e) => setAnchorEl(e.currentTarget)}
+ aria-label="expand options"
+ size="small"
+ sx={{
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: '-50%',
+ color: 'white',
+ }}
+ >
+ <ExpandLessIcon sx={{ backgroundColor: '#555555', borderRadius: '5px' }} />
+ </IconButton>
+ )}
+ <IconButton {...props} disableRipple={true}>
+ {Icon && <Icon fontSize="inherit" />}
+ </IconButton>
+ </Box>
+ </Box>
+ );
+})(({ theme }) => ({
+ color: 'white',
+}));
+
export const CancelPictureButton = (props: IconButtonProps) => {
return <RoundButton {...props} aria-label="remove picture" Icon={CancelIcon} size="large" />;
};
@@ -153,6 +262,7 @@
IconOn?: ComponentType<SvgIconProps>;
IconOff?: ComponentType<SvgIconProps>;
};
+
export const ToggleIconButton = ({
IconOn = RadioButtonChecked,
IconOff = RadioButtonUnchecked,