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 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /* @flow */
  2. import InlineDialog from '@atlaskit/inline-dialog';
  3. import React, { Component } from 'react';
  4. import { createToolbarEvent, sendAnalytics } from '../../../analytics';
  5. import { translate } from '../../../base/i18n';
  6. import { IconHorizontalPoints } from '../../../base/icons';
  7. import { connect } from '../../../base/redux';
  8. import Drawer from './Drawer';
  9. import DrawerPortal from './DrawerPortal';
  10. import ToolbarButton from './ToolbarButton';
  11. /**
  12. * The type of the React {@code Component} props of {@link OverflowMenuButton}.
  13. */
  14. type Props = {
  15. /**
  16. * ID of the menu that is controlled by this button.
  17. */
  18. ariaControls: String,
  19. /**
  20. * A child React Element to display within {@code InlineDialog}.
  21. */
  22. children: React$Node,
  23. /**
  24. * Whether or not the OverflowMenu popover should display.
  25. */
  26. isOpen: boolean,
  27. /**
  28. * Callback to change the visibility of the overflow menu.
  29. */
  30. onVisibilityChange: Function,
  31. /**
  32. * Whether to display the OverflowMenu as a drawer.
  33. */
  34. overflowDrawer: boolean,
  35. /**
  36. * Invoked to obtain translated strings.
  37. */
  38. t: Function
  39. };
  40. /**
  41. * A React {@code Component} for opening or closing the {@code OverflowMenu}.
  42. *
  43. * @extends Component
  44. */
  45. class OverflowMenuButton extends Component<Props> {
  46. /**
  47. * Initializes a new {@code OverflowMenuButton} instance.
  48. *
  49. * @param {Object} props - The read-only properties with which the new
  50. * instance is to be initialized.
  51. */
  52. constructor(props: Props) {
  53. super(props);
  54. // Bind event handlers so they are only bound once per instance.
  55. this._onCloseDialog = this._onCloseDialog.bind(this);
  56. this._onToggleDialogVisibility
  57. = this._onToggleDialogVisibility.bind(this);
  58. this._onEscClick = this._onEscClick.bind(this);
  59. }
  60. _onEscClick: (KeyboardEvent) => void;
  61. /**
  62. * Click handler for the more actions entries.
  63. *
  64. * @param {KeyboardEvent} event - Esc key click to close the popup.
  65. * @returns {void}
  66. */
  67. _onEscClick(event) {
  68. if (event.key === 'Escape' && this.props.isOpen) {
  69. event.preventDefault();
  70. event.stopPropagation();
  71. this._onCloseDialog();
  72. }
  73. }
  74. /**
  75. * Implements React's {@link Component#render()}.
  76. *
  77. * @inheritdoc
  78. * @returns {ReactElement}
  79. */
  80. render() {
  81. const { children, isOpen, overflowDrawer } = this.props;
  82. return (
  83. <div className = 'toolbox-button-wth-dialog'>
  84. {
  85. overflowDrawer ? (
  86. <>
  87. {this._renderToolbarButton()}
  88. <DrawerPortal>
  89. <Drawer
  90. canExpand = { true }
  91. isOpen = { isOpen }
  92. onClose = { this._onCloseDialog }>
  93. {children}
  94. </Drawer>
  95. </DrawerPortal>
  96. </>
  97. ) : (
  98. <InlineDialog
  99. content = { children }
  100. isOpen = { isOpen }
  101. onClose = { this._onCloseDialog }
  102. placement = 'top-end'>
  103. {this._renderToolbarButton()}
  104. </InlineDialog>
  105. )
  106. }
  107. </div>
  108. );
  109. }
  110. _renderToolbarButton: () => React$Node;
  111. /**
  112. * Renders the actual toolbar overflow menu button.
  113. *
  114. * @returns {ReactElement}
  115. */
  116. _renderToolbarButton() {
  117. const { ariaControls, isOpen, t } = this.props;
  118. return (
  119. <ToolbarButton
  120. accessibilityLabel =
  121. { t('toolbar.accessibilityLabel.moreActions') }
  122. aria-controls = { ariaControls }
  123. aria-haspopup = 'true'
  124. icon = { IconHorizontalPoints }
  125. onClick = { this._onToggleDialogVisibility }
  126. onKeyDown = { this._onEscClick }
  127. toggled = { isOpen }
  128. tooltip = { t('toolbar.moreActions') } />
  129. );
  130. }
  131. _onCloseDialog: () => void;
  132. /**
  133. * Callback invoked when {@code InlineDialog} signals that it should be
  134. * close.
  135. *
  136. * @private
  137. * @returns {void}
  138. */
  139. _onCloseDialog() {
  140. this.props.onVisibilityChange(false);
  141. }
  142. _onToggleDialogVisibility: () => void;
  143. /**
  144. * Callback invoked to signal that an event has occurred that should change
  145. * the visibility of the {@code InlineDialog} component.
  146. *
  147. * @private
  148. * @returns {void}
  149. */
  150. _onToggleDialogVisibility() {
  151. sendAnalytics(createToolbarEvent('overflow'));
  152. this.props.onVisibilityChange(!this.props.isOpen);
  153. }
  154. }
  155. /**
  156. * Maps (parts of) the Redux state to the associated props for the
  157. * {@code OverflowMenuButton} component.
  158. *
  159. * @param {Object} state - The Redux state.
  160. * @returns {Props}
  161. */
  162. function mapStateToProps(state) {
  163. const { overflowDrawer } = state['features/toolbox'];
  164. return {
  165. overflowDrawer
  166. };
  167. }
  168. export default translate(connect(mapStateToProps)(OverflowMenuButton));