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.

AbstractDialog.js 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. // @flow
  2. import PropTypes from 'prop-types';
  3. import { Component } from 'react';
  4. import { hideDialog } from '../actions';
  5. import { DIALOG_PROP_TYPES } from '../constants';
  6. /**
  7. * An abstract implementation of a dialog on Web/React and mobile/react-native.
  8. */
  9. export default class AbstractDialog extends Component {
  10. /**
  11. * {@code AbstractDialog} React {@code Component}'s prop types.
  12. *
  13. * @static
  14. */
  15. static propTypes = {
  16. ...DIALOG_PROP_TYPES,
  17. /**
  18. * The React {@code Component} children of {@code AbstractDialog}
  19. * which represents the dialog's body.
  20. */
  21. children: PropTypes.node,
  22. /**
  23. * Used to show/hide the dialog on cancel.
  24. */
  25. dispatch: PropTypes.func
  26. };
  27. _mounted: boolean;
  28. state = {
  29. };
  30. /**
  31. * Initializes a new {@code AbstractDialog} instance.
  32. *
  33. * @param {Object} props - The read-only React {@code Component} props with
  34. * which the new instance is to be initialized.
  35. */
  36. constructor(props: Object) {
  37. super(props);
  38. // Bind event handlers so they are only bound once per instance.
  39. this._onCancel = this._onCancel.bind(this);
  40. this._onSubmit = this._onSubmit.bind(this);
  41. this._onSubmitFulfilled = this._onSubmitFulfilled.bind(this);
  42. this._onSubmitRejected = this._onSubmitRejected.bind(this);
  43. }
  44. /**
  45. * Implements React's {@link Component#componentWillMount()}. Invoked
  46. * immediately before mounting occurs.
  47. *
  48. * @inheritdoc
  49. */
  50. componentWillMount() {
  51. this._mounted = true;
  52. }
  53. /**
  54. * Implements React's {@link Component#componentWillUnmount()}. Invoked
  55. * immediately before this component is unmounted and destroyed.
  56. *
  57. * @inheritdoc
  58. */
  59. componentWillUnmount() {
  60. this._mounted = false;
  61. }
  62. _onCancel: () => void;
  63. /**
  64. * Dispatches a redux action to hide this dialog when it's canceled.
  65. *
  66. * @protected
  67. * @returns {void}
  68. */
  69. _onCancel() {
  70. const { cancelDisabled, onCancel } = this.props;
  71. if ((typeof cancelDisabled === 'undefined' || !cancelDisabled)
  72. && (!onCancel || onCancel())) {
  73. this.props.dispatch(hideDialog());
  74. }
  75. }
  76. _onSubmit: (?string) => void;
  77. /**
  78. * Submits this {@code Dialog}. If the React {@code Component} prop
  79. * {@code onSubmit} is defined, the function that is the value of the prop
  80. * is invoked. If the function returns a {@code thenable}, then the
  81. * resolution of the {@code thenable} is awaited. If the submission
  82. * completes successfully, a redux action will be dispatched to hide this
  83. * dialog.
  84. *
  85. * @protected
  86. * @param {string} [value] - The submitted value if any.
  87. * @returns {void}
  88. */
  89. _onSubmit(value: ?string) {
  90. const { okDisabled, onSubmit } = this.props;
  91. if (typeof okDisabled === 'undefined' || !okDisabled) {
  92. this.setState({ submitting: true });
  93. // Invoke the React Compnent prop onSubmit if any.
  94. const r = !onSubmit || onSubmit(value);
  95. // If the invocation returns a thenable, await its resolution;
  96. // otherwise, treat the return value as a boolean indicating whether
  97. // the submission has completed successfully.
  98. let then;
  99. if (r) {
  100. switch (typeof r) {
  101. case 'function':
  102. case 'object':
  103. then = r.then;
  104. break;
  105. }
  106. }
  107. if (typeof then === 'function' && then.length === 2) {
  108. then.call(r, this._onSubmitFulfilled, this._onSubmitRejected);
  109. } else if (r) {
  110. this._onSubmitFulfilled();
  111. } else {
  112. this._onSubmitRejected();
  113. }
  114. }
  115. }
  116. _onSubmitFulfilled: () => void;
  117. /**
  118. * Notifies this {@code AbstractDialog} that it has been submitted
  119. * successfully. Dispatches a redux action to hide this dialog after it has
  120. * been submitted.
  121. *
  122. * @private
  123. * @returns {void}
  124. */
  125. _onSubmitFulfilled() {
  126. this._mounted && this.setState({ submitting: false });
  127. this.props.dispatch(hideDialog());
  128. }
  129. _onSubmitRejected: () => void;
  130. /**
  131. * Notifies this {@code AbstractDialog} that its submission has failed.
  132. *
  133. * @private
  134. * @returns {void}
  135. */
  136. _onSubmitRejected() {
  137. this._mounted && this.setState({ submitting: false });
  138. }
  139. }