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.js 6.5KB

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