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.

InfoDialogButton.web.js 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import InlineDialog from '@atlaskit/inline-dialog';
  2. import PropTypes from 'prop-types';
  3. import React, { Component } from 'react';
  4. import { connect } from 'react-redux';
  5. import { ToolbarButton, TOOLTIP_TO_POPUP_POSITION } from '../../toolbox';
  6. import { setInfoDialogVisibility } from '../actions';
  7. import InfoDialog from './InfoDialog';
  8. declare var interfaceConfig: Object;
  9. const { INITIAL_TOOLBAR_TIMEOUT } = interfaceConfig;
  10. /**
  11. * A configuration object to describe how {@code ToolbarButton} should render
  12. * the button.
  13. *
  14. * @type {object}
  15. */
  16. const DEFAULT_BUTTON_CONFIGURATION = {
  17. buttonName: 'info',
  18. classNames: [ 'button', 'icon-info' ],
  19. enabled: true,
  20. id: 'toolbar_button_info',
  21. tooltipKey: 'info.tooltip'
  22. };
  23. /**
  24. * A React Component for displaying a button which opens a dialog with
  25. * information about the conference and with ways to invite people.
  26. *
  27. * @extends Component
  28. */
  29. class InfoDialogButton extends Component {
  30. /**
  31. * {@code InfoDialogButton} component's property types.
  32. *
  33. * @static
  34. */
  35. static propTypes = {
  36. /**
  37. * Whether or not {@code InfoDialog} should be displayed.
  38. */
  39. _showDialog: PropTypes.bool,
  40. /**
  41. * Whether or not the toolbox, in which this component exists, are
  42. * visible.
  43. */
  44. _toolboxVisible: PropTypes.bool,
  45. /**
  46. * Invoked to toggle display of the info dialog
  47. */
  48. dispatch: PropTypes.func,
  49. /**
  50. * From which side tooltips should display. Will be re-used for
  51. * displaying the inline dialog for video quality adjustment.
  52. */
  53. tooltipPosition: PropTypes.string
  54. };
  55. /**
  56. * Initializes new {@code ToolbarButtonWithDialog} instance.
  57. *
  58. * @param {Object} props - The read-only properties with which the new
  59. * instance is to be initialized.
  60. */
  61. constructor(props) {
  62. super(props);
  63. /**
  64. * The timeout to automatically hide the {@code InfoDialog} if it has
  65. * not been interacted with.
  66. *
  67. * @type {timeoutID}
  68. */
  69. this._autoHideDialogTimeout = null;
  70. this.state = {
  71. /**
  72. * Whether or not the dialog has been interacted with somehow, such
  73. * as clicking or toggle display. A value of true will prevent the
  74. * dialog from being automatically hidden.
  75. */
  76. hasInteractedWithDialog: false
  77. };
  78. // Bind event handlers so they are only bound once for every instance.
  79. this._onDialogClose = this._onDialogClose.bind(this);
  80. this._onDialogMouseOver = this._onDialogMouseOver.bind(this);
  81. this._onDialogToggle = this._onDialogToggle.bind(this);
  82. }
  83. /**
  84. * Set a timeout to automatically hide the {@code InfoDialog}.
  85. *
  86. * @inheritdoc
  87. */
  88. componentDidMount() {
  89. this._autoHideDialogTimeout = setTimeout(() => {
  90. this._maybeHideDialog();
  91. }, INITIAL_TOOLBAR_TIMEOUT);
  92. }
  93. /**
  94. * Update the state when the {@code InfoDialog} visibility has been updated.
  95. *
  96. * @inheritdoc
  97. */
  98. componentWillReceiveProps(nextProps) {
  99. if (!this.state.hasInteractedWithDialog
  100. && (nextProps._showDialog !== this.props._showDialog)) {
  101. this.setState({ hasInteractedWithDialog: true });
  102. }
  103. if (!nextProps._toolboxVisible && this.props._toolboxVisible) {
  104. this._onDialogClose();
  105. }
  106. }
  107. /**
  108. * Clear the timeout to automatically show the {@code InfoDialog}.
  109. *
  110. * @inheritdoc
  111. */
  112. componentWillUnmount() {
  113. clearTimeout(this._autoHideDialogTimeout);
  114. }
  115. /**
  116. * Implements React's {@link Component#render()}.
  117. *
  118. * @inheritdoc
  119. * @returns {ReactElement}
  120. */
  121. render() {
  122. const { _showDialog, _toolboxVisible, tooltipPosition } = this.props;
  123. const buttonConfiguration = {
  124. ...DEFAULT_BUTTON_CONFIGURATION,
  125. classNames: [
  126. ...DEFAULT_BUTTON_CONFIGURATION.classNames,
  127. _showDialog ? 'toggled button-active' : ''
  128. ]
  129. };
  130. return (
  131. <InlineDialog
  132. content = { <InfoDialog
  133. onClose = { this._onDialogClose }
  134. onMouseOver = { this._onDialogMouseOver } /> }
  135. isOpen = { _toolboxVisible && _showDialog }
  136. onClose = { this._onDialogClose }
  137. onContentClick = { this._onDialogInteract }
  138. position = { TOOLTIP_TO_POPUP_POSITION[tooltipPosition] }>
  139. <ToolbarButton
  140. button = { buttonConfiguration }
  141. onClick = { this._onDialogToggle }
  142. tooltipPosition = { tooltipPosition } />
  143. </InlineDialog>
  144. );
  145. }
  146. /**
  147. * Callback invoked after a timeout to trigger hiding of the
  148. * {@code InfoDialog} if there has been no interaction with the dialog
  149. * and the dialog is currently showing.
  150. *
  151. * @private
  152. * @returns {void}
  153. */
  154. _maybeHideDialog() {
  155. if (!this.state.hasInteractedWithDialog && this.props._showDialog) {
  156. this._onDialogToggle();
  157. }
  158. }
  159. /**
  160. * Hides {@code InfoDialog}.
  161. *
  162. * @private
  163. * @returns {void}
  164. */
  165. _onDialogClose() {
  166. this.props.dispatch(setInfoDialogVisibility(false));
  167. }
  168. /**
  169. * Updates the internal state to mark the {@code InfoDialog} as having been
  170. * interacted with.
  171. *
  172. * @private
  173. * @returns {void}
  174. */
  175. _onDialogMouseOver() {
  176. if (!this.state.hasInteractedWithDialog) {
  177. this.setState({ hasInteractedWithDialog: true });
  178. }
  179. }
  180. /**
  181. * Toggles the display of {@code InfoDialog}.
  182. *
  183. * @private
  184. * @returns {void}
  185. */
  186. _onDialogToggle() {
  187. this.props.dispatch(setInfoDialogVisibility(!this.props._showDialog));
  188. }
  189. }
  190. /**
  191. * Maps (parts of) the Redux state to the associated {@code InfoDialogButton}
  192. * component's props.
  193. *
  194. * @param {Object} state - The Redux state.
  195. * @private
  196. * @returns {{
  197. * _showDialog: boolean,
  198. * _toolboxVisible: boolean
  199. * }}
  200. */
  201. function _mapStateToProps(state) {
  202. return {
  203. _showDialog: state['features/invite'].infoDialogVisible,
  204. _toolboxVisible: state['features/toolbox'].visible
  205. };
  206. }
  207. export default connect(_mapStateToProps)(InfoDialogButton);