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.3KB

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