Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

ContextMenu.tsx 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import { Theme } from '@mui/material';
  2. import React, { ReactNode, useEffect, useLayoutEffect, useRef, useState } from 'react';
  3. import { useSelector } from 'react-redux';
  4. import { makeStyles } from 'tss-react/mui';
  5. import { getComputedOuterHeight } from '../../../../participants-pane/functions';
  6. // eslint-disable-next-line lines-around-comment
  7. // @ts-ignore
  8. import { Drawer, JitsiPortal } from '../../../../toolbox/components/web';
  9. import { showOverflowDrawer } from '../../../../toolbox/functions.web';
  10. import participantsPaneTheme from '../../../components/themes/participantsPaneTheme.json';
  11. import { withPixelLineHeight } from '../../../styles/functions.web';
  12. type Props = {
  13. /**
  14. * Accessibility label for menu container.
  15. */
  16. accessibilityLabel?: string;
  17. /**
  18. * Children of the context menu.
  19. */
  20. children: ReactNode;
  21. /**
  22. * Class name for context menu. Used to overwrite default styles.
  23. */
  24. className?: string;
  25. /**
  26. * The entity for which the context menu is displayed.
  27. */
  28. entity?: Object;
  29. /**
  30. * Whether or not the menu is hidden. Used to overwrite the internal isHidden.
  31. */
  32. hidden?: boolean;
  33. /**
  34. * Whether or not the menu is already in a drawer.
  35. */
  36. inDrawer?: boolean;
  37. /**
  38. * Whether or not drawer should be open.
  39. */
  40. isDrawerOpen?: boolean;
  41. /**
  42. * Target elements against which positioning calculations are made.
  43. */
  44. offsetTarget?: HTMLElement;
  45. /**
  46. * Callback for click on an item in the menu.
  47. */
  48. onClick?: (e?: React.MouseEvent) => void;
  49. /**
  50. * Callback for drawer close.
  51. */
  52. onDrawerClose?: (e?: React.MouseEvent) => void;
  53. /**
  54. * Keydown handler.
  55. */
  56. onKeyDown?: (e?: React.KeyboardEvent) => void;
  57. /**
  58. * Callback for the mouse entering the component.
  59. */
  60. onMouseEnter?: (e?: React.MouseEvent) => void;
  61. /**
  62. * Callback for the mouse leaving the component.
  63. */
  64. onMouseLeave?: (e?: React.MouseEvent) => void;
  65. };
  66. const MAX_HEIGHT = 400;
  67. const useStyles = makeStyles()((theme: Theme) => {
  68. return {
  69. contextMenu: {
  70. backgroundColor: theme.palette.ui01,
  71. border: `1px solid ${theme.palette.ui04}`,
  72. borderRadius: `${Number(theme.shape.borderRadius)}px`,
  73. boxShadow: '0px 4px 25px 4px rgba(20, 20, 20, 0.6)',
  74. color: theme.palette.text01,
  75. ...withPixelLineHeight(theme.typography.bodyShortRegular),
  76. marginTop: `${(participantsPaneTheme.panePadding * 2) + theme.typography.bodyShortRegular.fontSize}px`,
  77. position: 'absolute',
  78. right: `${participantsPaneTheme.panePadding}px`,
  79. top: 0,
  80. zIndex: 2,
  81. maxHeight: `${MAX_HEIGHT}px`,
  82. overflowY: 'auto',
  83. padding: `${theme.spacing(2)} 0`
  84. },
  85. contextMenuHidden: {
  86. pointerEvents: 'none',
  87. visibility: 'hidden'
  88. },
  89. drawer: {
  90. paddingTop: '16px',
  91. '& > div': {
  92. ...withPixelLineHeight(theme.typography.bodyShortRegularLarge),
  93. '& svg': {
  94. fill: theme.palette.icon01
  95. }
  96. }
  97. }
  98. };
  99. });
  100. const ContextMenu = ({
  101. accessibilityLabel,
  102. children,
  103. className,
  104. entity,
  105. hidden,
  106. inDrawer,
  107. isDrawerOpen,
  108. offsetTarget,
  109. onClick,
  110. onKeyDown,
  111. onDrawerClose,
  112. onMouseEnter,
  113. onMouseLeave
  114. }: Props) => {
  115. const [ isHidden, setIsHidden ] = useState(true);
  116. const containerRef = useRef<HTMLDivElement | null>(null);
  117. const { classes: styles, cx } = useStyles();
  118. const _overflowDrawer = useSelector(showOverflowDrawer);
  119. useLayoutEffect(() => {
  120. if (_overflowDrawer) {
  121. return;
  122. }
  123. if (entity && offsetTarget
  124. && containerRef.current
  125. && offsetTarget?.offsetParent
  126. && offsetTarget.offsetParent instanceof HTMLElement
  127. ) {
  128. const { current: container } = containerRef;
  129. const { offsetTop, offsetParent: { offsetHeight, scrollTop } } = offsetTarget;
  130. const outerHeight = getComputedOuterHeight(container);
  131. const height = Math.min(MAX_HEIGHT, outerHeight);
  132. container.style.top = offsetTop + height > offsetHeight + scrollTop
  133. ? `${offsetTop - outerHeight}`
  134. : `${offsetTop}`;
  135. setIsHidden(false);
  136. } else {
  137. hidden === undefined && setIsHidden(true);
  138. }
  139. }, [ entity, offsetTarget, _overflowDrawer ]);
  140. useEffect(() => {
  141. if (hidden !== undefined) {
  142. setIsHidden(hidden);
  143. }
  144. }, [ hidden ]);
  145. if (_overflowDrawer && inDrawer) {
  146. return (<div
  147. className = { styles.drawer }
  148. onClick = { onDrawerClose }>
  149. {children}
  150. </div>);
  151. }
  152. return _overflowDrawer
  153. ? <JitsiPortal>
  154. <Drawer
  155. isOpen = { isDrawerOpen && _overflowDrawer }
  156. onClose = { onDrawerClose }>
  157. <div
  158. className = { styles.drawer }
  159. onClick = { onDrawerClose }>
  160. {children}
  161. </div>
  162. </Drawer>
  163. </JitsiPortal>
  164. : <div
  165. aria-label = { accessibilityLabel }
  166. className = { cx(participantsPaneTheme.ignoredChildClassName,
  167. styles.contextMenu,
  168. isHidden && styles.contextMenuHidden,
  169. className
  170. ) }
  171. onClick = { onClick }
  172. onKeyDown = { onKeyDown }
  173. onMouseEnter = { onMouseEnter }
  174. onMouseLeave = { onMouseLeave }
  175. ref = { containerRef }>
  176. {children}
  177. </div>;
  178. };
  179. export default ContextMenu;