Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

AbstractDialog.ts 4.4KB

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