2022-08-26 09:54:03 +00:00
|
|
|
/* eslint-disable lines-around-comment */
|
2022-09-13 07:36:00 +00:00
|
|
|
|
|
|
|
import { Theme } from '@mui/material';
|
2022-08-26 09:54:03 +00:00
|
|
|
import React, { ReactNode, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
2021-10-22 13:23:52 +00:00
|
|
|
import { useSelector } from 'react-redux';
|
2022-09-13 07:36:00 +00:00
|
|
|
import { makeStyles } from 'tss-react/mui';
|
2021-10-22 13:23:52 +00:00
|
|
|
|
2022-10-06 10:09:40 +00:00
|
|
|
import { getComputedOuterHeight } from '../../../../participants-pane/functions';
|
2022-08-26 09:54:03 +00:00
|
|
|
// @ts-ignore
|
2022-10-06 10:09:40 +00:00
|
|
|
import { Drawer, JitsiPortal } from '../../../../toolbox/components/web';
|
2022-08-26 09:54:03 +00:00
|
|
|
// @ts-ignore
|
2022-10-06 10:09:40 +00:00
|
|
|
import { showOverflowDrawer } from '../../../../toolbox/functions.web';
|
|
|
|
import participantsPaneTheme from '../../../components/themes/participantsPaneTheme.json';
|
|
|
|
import { withPixelLineHeight } from '../../../styles/functions.web';
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
type Props = {
|
|
|
|
|
2022-04-05 12:19:03 +00:00
|
|
|
/**
|
|
|
|
* Accessibility label for menu container.
|
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
accessibilityLabel?: string;
|
2022-04-05 12:19:03 +00:00
|
|
|
|
2021-10-22 13:23:52 +00:00
|
|
|
/**
|
|
|
|
* Children of the context menu.
|
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
children: ReactNode;
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Class name for context menu. Used to overwrite default styles.
|
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
className?: string;
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The entity for which the context menu is displayed.
|
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
entity?: Object;
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether or not the menu is hidden. Used to overwrite the internal isHidden.
|
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
hidden?: boolean;
|
2021-10-22 13:23:52 +00:00
|
|
|
|
2021-12-15 13:18:41 +00:00
|
|
|
/**
|
|
|
|
* Whether or not the menu is already in a drawer.
|
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
inDrawer?: boolean;
|
2021-12-15 13:18:41 +00:00
|
|
|
|
2021-10-22 13:23:52 +00:00
|
|
|
/**
|
|
|
|
* Whether or not drawer should be open.
|
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
isDrawerOpen?: boolean;
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
/**
|
2021-11-04 21:10:43 +00:00
|
|
|
* Target elements against which positioning calculations are made.
|
2021-10-22 13:23:52 +00:00
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
offsetTarget?: HTMLElement;
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
/**
|
2021-11-04 21:10:43 +00:00
|
|
|
* Callback for click on an item in the menu.
|
2021-10-22 13:23:52 +00:00
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
onClick?: (e?: React.MouseEvent) => void;
|
2021-10-22 13:23:52 +00:00
|
|
|
|
2022-04-05 12:19:03 +00:00
|
|
|
/**
|
2022-08-26 09:54:03 +00:00
|
|
|
* Callback for drawer close.
|
2022-04-05 12:19:03 +00:00
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
onDrawerClose?: (e?: React.MouseEvent) => void;
|
2022-04-05 12:19:03 +00:00
|
|
|
|
2021-10-22 13:23:52 +00:00
|
|
|
/**
|
2022-08-26 09:54:03 +00:00
|
|
|
* Keydown handler.
|
2021-10-22 13:23:52 +00:00
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
onKeyDown?: (e?: React.KeyboardEvent) => void;
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
/**
|
2021-11-04 21:10:43 +00:00
|
|
|
* Callback for the mouse entering the component.
|
2021-10-22 13:23:52 +00:00
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
onMouseEnter?: (e?: React.MouseEvent) => void;
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
/**
|
2021-11-04 21:10:43 +00:00
|
|
|
* Callback for the mouse leaving the component.
|
2021-10-22 13:23:52 +00:00
|
|
|
*/
|
2022-09-08 09:52:36 +00:00
|
|
|
onMouseLeave?: (e?: React.MouseEvent) => void;
|
2021-10-22 13:23:52 +00:00
|
|
|
};
|
|
|
|
|
2022-02-15 11:23:40 +00:00
|
|
|
const MAX_HEIGHT = 400;
|
|
|
|
|
2022-09-13 07:36:00 +00:00
|
|
|
const useStyles = makeStyles()((theme: Theme) => {
|
2021-10-22 13:23:52 +00:00
|
|
|
return {
|
|
|
|
contextMenu: {
|
2022-10-06 10:09:40 +00:00
|
|
|
backgroundColor: theme.palette.ui01,
|
|
|
|
border: `1px solid ${theme.palette.ui04}`,
|
|
|
|
borderRadius: `${Number(theme.shape.borderRadius)}px`,
|
|
|
|
boxShadow: '0px 4px 25px 4px rgba(20, 20, 20, 0.6)',
|
2021-10-22 13:23:52 +00:00
|
|
|
color: theme.palette.text01,
|
2022-09-13 07:36:00 +00:00
|
|
|
...withPixelLineHeight(theme.typography.bodyShortRegular),
|
2021-10-22 13:23:52 +00:00
|
|
|
marginTop: `${(participantsPaneTheme.panePadding * 2) + theme.typography.bodyShortRegular.fontSize}px`,
|
|
|
|
position: 'absolute',
|
|
|
|
right: `${participantsPaneTheme.panePadding}px`,
|
|
|
|
top: 0,
|
2022-02-15 11:23:40 +00:00
|
|
|
zIndex: 2,
|
|
|
|
maxHeight: `${MAX_HEIGHT}px`,
|
2022-10-06 10:09:40 +00:00
|
|
|
overflowY: 'auto',
|
|
|
|
padding: `${theme.spacing(2)} 0`
|
2021-10-22 13:23:52 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
contextMenuHidden: {
|
|
|
|
pointerEvents: 'none',
|
|
|
|
visibility: 'hidden'
|
|
|
|
},
|
|
|
|
|
|
|
|
drawer: {
|
2022-10-06 10:09:40 +00:00
|
|
|
paddingTop: '16px',
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
'& > div': {
|
2022-09-13 07:36:00 +00:00
|
|
|
...withPixelLineHeight(theme.typography.bodyShortRegularLarge),
|
2021-10-22 13:23:52 +00:00
|
|
|
|
|
|
|
'& svg': {
|
|
|
|
fill: theme.palette.icon01
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
const ContextMenu = ({
|
2022-04-05 12:19:03 +00:00
|
|
|
accessibilityLabel,
|
2021-10-22 13:23:52 +00:00
|
|
|
children,
|
|
|
|
className,
|
|
|
|
entity,
|
|
|
|
hidden,
|
2021-12-15 13:18:41 +00:00
|
|
|
inDrawer,
|
2021-10-22 13:23:52 +00:00
|
|
|
isDrawerOpen,
|
|
|
|
offsetTarget,
|
|
|
|
onClick,
|
2022-04-05 12:19:03 +00:00
|
|
|
onKeyDown,
|
2021-10-22 13:23:52 +00:00
|
|
|
onDrawerClose,
|
|
|
|
onMouseEnter,
|
|
|
|
onMouseLeave
|
|
|
|
}: Props) => {
|
|
|
|
const [ isHidden, setIsHidden ] = useState(true);
|
|
|
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
2022-09-13 07:36:00 +00:00
|
|
|
const { classes: styles, cx } = useStyles();
|
2021-10-22 13:23:52 +00:00
|
|
|
const _overflowDrawer = useSelector(showOverflowDrawer);
|
|
|
|
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
if (_overflowDrawer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (entity && offsetTarget
|
|
|
|
&& containerRef.current
|
|
|
|
&& offsetTarget?.offsetParent
|
|
|
|
&& offsetTarget.offsetParent instanceof HTMLElement
|
|
|
|
) {
|
|
|
|
const { current: container } = containerRef;
|
|
|
|
const { offsetTop, offsetParent: { offsetHeight, scrollTop } } = offsetTarget;
|
|
|
|
const outerHeight = getComputedOuterHeight(container);
|
2022-02-15 11:23:40 +00:00
|
|
|
const height = Math.min(MAX_HEIGHT, outerHeight);
|
2021-10-22 13:23:52 +00:00
|
|
|
|
2022-02-15 11:23:40 +00:00
|
|
|
container.style.top = offsetTop + height > offsetHeight + scrollTop
|
2021-10-22 13:23:52 +00:00
|
|
|
? `${offsetTop - outerHeight}`
|
|
|
|
: `${offsetTop}`;
|
|
|
|
|
|
|
|
setIsHidden(false);
|
|
|
|
} else {
|
2022-03-30 11:50:41 +00:00
|
|
|
hidden === undefined && setIsHidden(true);
|
2021-10-22 13:23:52 +00:00
|
|
|
}
|
|
|
|
}, [ entity, offsetTarget, _overflowDrawer ]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (hidden !== undefined) {
|
|
|
|
setIsHidden(hidden);
|
|
|
|
}
|
|
|
|
}, [ hidden ]);
|
|
|
|
|
2021-12-15 13:18:41 +00:00
|
|
|
if (_overflowDrawer && inDrawer) {
|
|
|
|
return (<div
|
|
|
|
className = { styles.drawer }
|
|
|
|
onClick = { onDrawerClose }>
|
|
|
|
{children}
|
|
|
|
</div>);
|
|
|
|
}
|
|
|
|
|
2021-10-22 13:23:52 +00:00
|
|
|
return _overflowDrawer
|
|
|
|
? <JitsiPortal>
|
|
|
|
<Drawer
|
|
|
|
isOpen = { isDrawerOpen && _overflowDrawer }
|
|
|
|
onClose = { onDrawerClose }>
|
2021-11-01 09:39:19 +00:00
|
|
|
<div
|
|
|
|
className = { styles.drawer }
|
|
|
|
onClick = { onDrawerClose }>
|
2021-10-22 13:23:52 +00:00
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
</Drawer>
|
|
|
|
</JitsiPortal>
|
|
|
|
: <div
|
2022-04-05 12:19:03 +00:00
|
|
|
aria-label = { accessibilityLabel }
|
2022-09-13 07:36:00 +00:00
|
|
|
className = { cx(participantsPaneTheme.ignoredChildClassName,
|
2021-10-22 13:23:52 +00:00
|
|
|
styles.contextMenu,
|
|
|
|
isHidden && styles.contextMenuHidden,
|
|
|
|
className
|
|
|
|
) }
|
|
|
|
onClick = { onClick }
|
2022-04-05 12:19:03 +00:00
|
|
|
onKeyDown = { onKeyDown }
|
2021-10-22 13:23:52 +00:00
|
|
|
onMouseEnter = { onMouseEnter }
|
|
|
|
onMouseLeave = { onMouseLeave }
|
|
|
|
ref = { containerRef }>
|
|
|
|
{children}
|
2022-09-13 07:36:00 +00:00
|
|
|
</div>;
|
2021-10-22 13:23:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default ContextMenu;
|