You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

OverflowMenuButton.tsx 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /* eslint-disable lines-around-comment */
  2. import InlineDialog from '@atlaskit/inline-dialog';
  3. import { withStyles } from '@material-ui/styles';
  4. import React, { Component, ReactElement } from 'react';
  5. import { WithTranslation } from 'react-i18next';
  6. // @ts-ignore
  7. import { createToolbarEvent, sendAnalytics } from '../../../analytics';
  8. import { translate } from '../../../base/i18n/functions';
  9. import { connect } from '../../../base/redux/functions';
  10. // @ts-ignore
  11. import { ReactionEmoji, ReactionsMenu } from '../../../reactions/components';
  12. import { type ReactionEmojiProps, REACTIONS_MENU_HEIGHT } from '../../../reactions/constants';
  13. import { getReactionsQueue } from '../../../reactions/functions.any';
  14. import { DRAWER_MAX_HEIGHT } from '../../constants';
  15. // @ts-ignore
  16. import Drawer from './Drawer';
  17. // @ts-ignore
  18. import JitsiPortal from './JitsiPortal';
  19. // @ts-ignore
  20. import OverflowToggleButton from './OverflowToggleButton';
  21. /**
  22. * The type of the React {@code Component} props of {@link OverflowMenuButton}.
  23. */
  24. interface Props extends WithTranslation {
  25. /**
  26. * ID of the menu that is controlled by this button.
  27. */
  28. ariaControls: String,
  29. /**
  30. * A child React Element to display within {@code InlineDialog}.
  31. */
  32. children: ReactElement,
  33. /**
  34. * An object containing the CSS classes.
  35. */
  36. classes: any,
  37. /**
  38. * Whether or not the OverflowMenu popover should display.
  39. */
  40. isOpen: boolean,
  41. /**
  42. * Callback to change the visibility of the overflow menu.
  43. */
  44. onVisibilityChange: Function,
  45. /**
  46. * Whether to display the OverflowMenu as a drawer.
  47. */
  48. overflowDrawer: boolean,
  49. /**
  50. * The array of reactions to be displayed.
  51. */
  52. reactionsQueue: Array<ReactionEmojiProps>,
  53. /**
  54. * Whether or not to display the reactions in the mobile menu.
  55. */
  56. showMobileReactions: boolean
  57. }
  58. const styles = () => {
  59. return {
  60. overflowMenuDrawer: {
  61. overflowY: 'auto',
  62. height: `calc(${DRAWER_MAX_HEIGHT} - ${REACTIONS_MENU_HEIGHT}px - 16px)`
  63. }
  64. };
  65. };
  66. /**
  67. * A React {@code Component} for opening or closing the {@code OverflowMenu}.
  68. *
  69. * @augments Component
  70. */
  71. class OverflowMenuButton extends Component<Props> {
  72. /**
  73. * Initializes a new {@code OverflowMenuButton} instance.
  74. *
  75. * @param {Object} props - The read-only properties with which the new
  76. * instance is to be initialized.
  77. */
  78. constructor(props: Props) {
  79. super(props);
  80. // Bind event handlers so they are only bound once per instance.
  81. this._onCloseDialog = this._onCloseDialog.bind(this);
  82. this._toggleDialogVisibility
  83. = this._toggleDialogVisibility.bind(this);
  84. this._onEscClick = this._onEscClick.bind(this);
  85. }
  86. /**
  87. * Click handler for the more actions entries.
  88. *
  89. * @param {KeyboardEvent} event - Esc key click to close the popup.
  90. * @returns {void}
  91. */
  92. _onEscClick(event: React.KeyboardEvent) {
  93. if (event.key === 'Escape' && this.props.isOpen) {
  94. event.preventDefault();
  95. event.stopPropagation();
  96. this._onCloseDialog();
  97. }
  98. }
  99. /**
  100. * Implements React's {@link Component#render()}.
  101. *
  102. * @inheritdoc
  103. * @returns {ReactElement}
  104. */
  105. render() {
  106. const { children, classes, isOpen, overflowDrawer, reactionsQueue, showMobileReactions } = this.props;
  107. return (
  108. <div className = 'toolbox-button-wth-dialog context-menu'>
  109. {
  110. overflowDrawer ? (
  111. <>
  112. <OverflowToggleButton
  113. handleClick = { this._toggleDialogVisibility }
  114. isOpen = { isOpen }
  115. onKeyDown = { this._onEscClick } />
  116. <JitsiPortal>
  117. <Drawer
  118. isOpen = { isOpen }
  119. onClose = { this._onCloseDialog }>
  120. <>
  121. <div className = { classes.overflowMenuDrawer }>
  122. {children}
  123. </div>
  124. {showMobileReactions && <ReactionsMenu overflowMenu = { true } />}
  125. </>
  126. </Drawer>
  127. {showMobileReactions && <div className = 'reactions-animations-container'>
  128. {reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
  129. index = { index }
  130. key = { uid }
  131. reaction = { reaction }
  132. uid = { uid } />))}
  133. </div>}
  134. </JitsiPortal>
  135. </>
  136. ) : (
  137. <InlineDialog
  138. content = { children }
  139. isOpen = { isOpen }
  140. onClose = { this._onCloseDialog }
  141. placement = 'top-end'>
  142. <OverflowToggleButton
  143. handleClick = { this._toggleDialogVisibility }
  144. isOpen = { isOpen }
  145. onKeyDown = { this._onEscClick } />
  146. </InlineDialog>
  147. )
  148. }
  149. </div>
  150. );
  151. }
  152. /**
  153. * Callback invoked when {@code InlineDialog} signals that it should be
  154. * close.
  155. *
  156. * @private
  157. * @returns {void}
  158. */
  159. _onCloseDialog() {
  160. this.props.onVisibilityChange(false);
  161. }
  162. /**
  163. * Callback invoked to signal that an event has occurred that should change
  164. * the visibility of the {@code InlineDialog} component.
  165. *
  166. * @private
  167. * @returns {void}
  168. */
  169. _toggleDialogVisibility() {
  170. sendAnalytics(createToolbarEvent('overflow'));
  171. this.props.onVisibilityChange(!this.props.isOpen);
  172. }
  173. }
  174. /**
  175. * Maps (parts of) the Redux state to the associated props for the
  176. * {@code OverflowMenuButton} component.
  177. *
  178. * @param {Object} state - The Redux state.
  179. * @returns {Props}
  180. */
  181. function mapStateToProps(state: any) {
  182. const { overflowDrawer } = state['features/toolbox'];
  183. return {
  184. overflowDrawer,
  185. reactionsQueue: getReactionsQueue(state)
  186. };
  187. }
  188. // @ts-ignore
  189. export default withStyles(styles)(translate(connect(mapStateToProps)(OverflowMenuButton)));