Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

OverflowMenuButton.tsx 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. import React, { useCallback } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import { useDispatch, useSelector } from 'react-redux';
  4. import { makeStyles } from 'tss-react/mui';
  5. import { createToolbarEvent } from '../../../analytics/AnalyticsEvents';
  6. import { sendAnalytics } from '../../../analytics/functions';
  7. import Popover from '../../../base/popover/components/Popover.web';
  8. import ContextMenu from '../../../base/ui/components/web/ContextMenu';
  9. import ContextMenuItemGroup from '../../../base/ui/components/web/ContextMenuItemGroup';
  10. import { setGifMenuVisibility } from '../../../gifs/actions';
  11. import { isGifsMenuOpen } from '../../../gifs/functions.web';
  12. import ReactionEmoji from '../../../reactions/components/web/ReactionEmoji';
  13. import ReactionsMenu from '../../../reactions/components/web/ReactionsMenu';
  14. import {
  15. GIFS_MENU_HEIGHT_IN_OVERFLOW_MENU,
  16. RAISE_HAND_ROW_HEIGHT,
  17. REACTIONS_MENU_HEIGHT_DRAWER,
  18. REACTIONS_MENU_HEIGHT_IN_OVERFLOW_MENU
  19. } from '../../../reactions/constants';
  20. import { getReactionsQueue } from '../../../reactions/functions.any';
  21. import { IReactionsMenuParent } from '../../../reactions/types';
  22. import { DRAWER_MAX_HEIGHT } from '../../constants';
  23. import { showOverflowDrawer } from '../../functions.web';
  24. import Drawer from './Drawer';
  25. import JitsiPortal from './JitsiPortal';
  26. import OverflowToggleButton from './OverflowToggleButton';
  27. /**
  28. * The type of the React {@code Component} props of {@link OverflowMenuButton}.
  29. */
  30. interface IProps {
  31. /**
  32. * ID of the menu that is controlled by this button.
  33. */
  34. ariaControls: string;
  35. /**
  36. * Information about the buttons that need to be rendered in the overflow menu.
  37. */
  38. buttons: Object[];
  39. /**
  40. * Whether or not the OverflowMenu popover should display.
  41. */
  42. isOpen: boolean;
  43. /**
  44. * Esc key handler.
  45. */
  46. onToolboxEscKey: (e?: React.KeyboardEvent) => void;
  47. /**
  48. * Callback to change the visibility of the overflow menu.
  49. */
  50. onVisibilityChange: Function;
  51. /**
  52. * Whether to show the raise hand in the reactions menu or not.
  53. */
  54. showRaiseHandInReactionsMenu: boolean;
  55. /**
  56. * Whether or not to display the reactions menu.
  57. */
  58. showReactionsMenu: boolean;
  59. }
  60. const useStyles = makeStyles<{ overflowDrawer: boolean; reactionsMenuHeight: number; }>()(
  61. (_theme, { reactionsMenuHeight, overflowDrawer }) => {
  62. return {
  63. overflowMenuDrawer: {
  64. overflowY: 'scroll',
  65. height: `calc(${DRAWER_MAX_HEIGHT})`
  66. },
  67. contextMenu: {
  68. position: 'relative' as const,
  69. right: 'auto',
  70. margin: 0,
  71. marginBottom: '8px',
  72. maxHeight: overflowDrawer ? undefined : 'calc(100dvh - 100px)',
  73. paddingBottom: overflowDrawer ? undefined : 0,
  74. minWidth: '240px',
  75. overflow: 'hidden'
  76. },
  77. content: {
  78. position: 'relative',
  79. maxHeight: overflowDrawer
  80. ? `calc(100% - ${reactionsMenuHeight}px - 16px)` : `calc(100dvh - 100px - ${reactionsMenuHeight}px)`,
  81. overflowY: 'auto'
  82. },
  83. footer: {
  84. position: 'absolute',
  85. bottom: 0,
  86. left: 0,
  87. right: 0
  88. },
  89. reactionsPadding: {
  90. height: `${reactionsMenuHeight}px`
  91. }
  92. };
  93. });
  94. const OverflowMenuButton = ({
  95. buttons,
  96. isOpen,
  97. onToolboxEscKey,
  98. onVisibilityChange,
  99. showRaiseHandInReactionsMenu,
  100. showReactionsMenu
  101. }: IProps) => {
  102. const overflowDrawer = useSelector(showOverflowDrawer);
  103. const reactionsQueue = useSelector(getReactionsQueue);
  104. const isGiphyVisible = useSelector(isGifsMenuOpen);
  105. const dispatch = useDispatch();
  106. const onCloseDialog = useCallback(() => {
  107. onVisibilityChange(false);
  108. if (isGiphyVisible && !overflowDrawer) {
  109. dispatch(setGifMenuVisibility(false));
  110. }
  111. }, [ onVisibilityChange, setGifMenuVisibility, isGiphyVisible, overflowDrawer, dispatch ]);
  112. const onOpenDialog = useCallback(() => {
  113. onVisibilityChange(true);
  114. }, [ onVisibilityChange ]);
  115. const onEscClick = useCallback((event: React.KeyboardEvent) => {
  116. if (event.key === 'Escape' && isOpen) {
  117. event.preventDefault();
  118. event.stopPropagation();
  119. onCloseDialog();
  120. }
  121. }, [ onCloseDialog ]);
  122. const toggleDialogVisibility = useCallback(() => {
  123. sendAnalytics(createToolbarEvent('overflow'));
  124. onVisibilityChange(!isOpen);
  125. }, [ isOpen, onVisibilityChange ]);
  126. const toolbarAccLabel = 'toolbar.accessibilityLabel.moreActionsMenu';
  127. const { t } = useTranslation();
  128. let reactionsMenuHeight = 0;
  129. if (showReactionsMenu) {
  130. reactionsMenuHeight = REACTIONS_MENU_HEIGHT_DRAWER;
  131. if (!overflowDrawer) {
  132. reactionsMenuHeight = REACTIONS_MENU_HEIGHT_IN_OVERFLOW_MENU;
  133. }
  134. if (!showRaiseHandInReactionsMenu) {
  135. reactionsMenuHeight -= RAISE_HAND_ROW_HEIGHT;
  136. }
  137. if (!overflowDrawer && isGiphyVisible) {
  138. reactionsMenuHeight += GIFS_MENU_HEIGHT_IN_OVERFLOW_MENU;
  139. }
  140. }
  141. const { classes } = useStyles({
  142. reactionsMenuHeight,
  143. overflowDrawer
  144. });
  145. const groupsJSX = buttons.map((buttonGroup: any) => (
  146. <ContextMenuItemGroup key = { `group-${buttonGroup[0].group}` }>
  147. {buttonGroup.map(({ key, Content, ...rest }: { Content: React.ElementType; key: string; }) => {
  148. const props: { buttonKey?: string; contextMenu?: boolean; showLabel?: boolean; } = { ...rest };
  149. if (key !== 'reactions') {
  150. props.buttonKey = key;
  151. props.contextMenu = true;
  152. props.showLabel = true;
  153. }
  154. return (
  155. <Content
  156. { ...props }
  157. key = { key } />);
  158. })}
  159. </ContextMenuItemGroup>));
  160. const overflowMenu = groupsJSX && (
  161. <ContextMenu
  162. accessibilityLabel = { t(toolbarAccLabel) }
  163. className = { classes.contextMenu }
  164. hidden = { false }
  165. id = 'overflow-context-menu'
  166. inDrawer = { overflowDrawer }
  167. onKeyDown = { onToolboxEscKey }>
  168. <div className = { classes.content }>
  169. { groupsJSX }
  170. </div>
  171. {
  172. showReactionsMenu && (<div className = { classes.footer }>
  173. <ReactionsMenu
  174. parent = {
  175. overflowDrawer ? IReactionsMenuParent.OverflowDrawer : IReactionsMenuParent.OverflowMenu }
  176. showRaisedHand = { showRaiseHandInReactionsMenu } />
  177. </div>)
  178. }
  179. </ContextMenu>);
  180. if (overflowDrawer) {
  181. return (
  182. <div className = 'toolbox-button-wth-dialog context-menu'>
  183. <>
  184. <OverflowToggleButton
  185. handleClick = { toggleDialogVisibility }
  186. isOpen = { isOpen }
  187. onKeyDown = { onEscClick } />
  188. <JitsiPortal>
  189. <Drawer
  190. isOpen = { isOpen }
  191. onClose = { onCloseDialog }>
  192. <>
  193. <div className = { classes.overflowMenuDrawer }>
  194. { overflowMenu }
  195. <div className = { classes.reactionsPadding } />
  196. </div>
  197. </>
  198. </Drawer>
  199. {showReactionsMenu && <div className = 'reactions-animations-overflow-container'>
  200. {reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
  201. index = { index }
  202. key = { uid }
  203. reaction = { reaction }
  204. uid = { uid } />))}
  205. </div>}
  206. </JitsiPortal>
  207. </>
  208. </div>
  209. );
  210. }
  211. return (
  212. <div className = 'toolbox-button-wth-dialog context-menu'>
  213. <Popover
  214. content = { overflowMenu }
  215. headingId = 'overflow-context-menu'
  216. onPopoverClose = { onCloseDialog }
  217. onPopoverOpen = { onOpenDialog }
  218. position = 'top'
  219. trigger = 'click'
  220. visible = { isOpen }>
  221. <OverflowToggleButton
  222. isMenuButton = { true }
  223. isOpen = { isOpen }
  224. onKeyDown = { onEscClick } />
  225. </Popover>
  226. {showReactionsMenu && <div className = 'reactions-animations-container'>
  227. {reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
  228. index = { index }
  229. key = { uid }
  230. reaction = { reaction }
  231. uid = { uid } />))}
  232. </div>}
  233. </div>
  234. );
  235. };
  236. export default OverflowMenuButton;