@e1himself/react-promise-modal 2.0.2 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,15 +8,15 @@ type Props = {
8
8
  };
9
9
  type RenderProps = {
10
10
  isOpen: boolean;
11
- stage: `${Stage.CLOSED | Stage.OPEN | Stage.OPENING | Stage.CLOSING}`;
12
11
  onClose: () => void;
13
12
  };
14
13
  export declare enum Stage {
15
14
  UNMOUNTED = "unmounted",
15
+ STANDBY = "standby",// same as "closed", but before "opening"
16
16
  OPEN = "open",
17
17
  OPENING = "opening",
18
18
  CLOSING = "closing",
19
19
  CLOSED = "closed"
20
20
  }
21
- export declare function ModalTransitions({ isOpen: shouldOpen, onClosed, transitionDuration, render }: Props): React.ReactElement<any, string | React.JSXElementConstructor<any>> | null;
21
+ export declare function ModalTransitions({ isOpen: shouldOpen, onClosed, transitionDuration, render }: Props): React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | null;
22
22
  export {};
@@ -4,11 +4,10 @@ var _react = require("react");
4
4
  var React = _react;
5
5
  var _lib = require("./lib");
6
6
  var delay = _lib.delay;
7
- var noop = _lib.noop;
8
- var _useIsMounted = require("./useIsMounted");
9
- var useIsMounted = _useIsMounted.useIsMounted;
10
7
  var _useLatest = require("./useLatest");
11
8
  var useLatest = _useLatest.useLatest;
9
+ var _useNextFrameEffect = require("./useNextFrameEffect");
10
+ var useNextFrameEffect = _useNextFrameEffect.useNextFrameEffect;
12
11
  function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
13
12
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
14
13
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
@@ -17,10 +16,12 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
17
16
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
18
17
  var Stage = /*#__PURE__*/exports.Stage = function (Stage) {
19
18
  Stage["UNMOUNTED"] = "unmounted";
19
+ Stage["STANDBY"] = "standby";
20
+ // same as "closed", but before "opening"
20
21
  Stage["OPEN"] = "open";
21
22
  Stage["OPENING"] = "opening";
22
23
  Stage["CLOSING"] = "closing";
23
- Stage["CLOSED"] = "closed";
24
+ Stage["CLOSED"] = "closed"; // same as "standby", but after "closing"
24
25
  return Stage;
25
26
  }({});
26
27
  function ModalTransitions(_ref) {
@@ -28,7 +29,6 @@ function ModalTransitions(_ref) {
28
29
  onClosed = _ref.onClosed,
29
30
  transitionDuration = _ref.transitionDuration,
30
31
  render = _ref.render;
31
- var isMounted = useIsMounted();
32
32
  var _React$useState = React.useState(Stage.UNMOUNTED),
33
33
  _React$useState2 = _slicedToArray(_React$useState, 2),
34
34
  stage = _React$useState2[0],
@@ -39,72 +39,84 @@ function ModalTransitions(_ref) {
39
39
  transitionDuration: transitionDuration
40
40
  });
41
41
  var onOpen = React.useCallback(function () {
42
- if (!isMounted()) {
43
- return noop;
44
- }
45
- if (refs.current.stage === Stage.OPENING || refs.current.stage === Stage.OPEN) {
46
- return noop; // Nothing to do
47
- }
48
- var cancel = false;
49
-
50
- // First, render it closed
51
- setStage(Stage.CLOSED);
42
+ setStage(function (stage) {
43
+ // If it's UNMOUNTED or CLOSED: switch to STANDBY to prepare for OPENING.
44
+ if (stage === Stage.UNMOUNTED || stage === Stage.CLOSED) {
45
+ setStage(Stage.STANDBY);
46
+ }
52
47
 
53
- // Then, immediately start "opening" sequence
54
- delay(0).then(function () {
55
- return setStage(Stage.OPENING);
56
- }).then(function () {
57
- return delay(refs.current.transitionDuration);
58
- }) // Wait another `transitionDelay`
59
- .then(function () {
60
- if (cancel || !isMounted()) {
61
- return;
48
+ // If it's CLOSING: override and start OPENING.
49
+ if (stage === Stage.CLOSING) {
50
+ setStage(Stage.OPENING);
62
51
  }
63
- // Mark it open.
64
- setStage(Stage.OPEN);
52
+
53
+ // If opening is requested while it's STANDBY, OPENING or already OPEN: do nothing.
54
+ // The `useLayoutEffect()` hook below will do the rest.
55
+ return stage;
65
56
  });
66
- return function () {
67
- cancel = true;
68
- };
69
57
  }, []);
70
58
  var onClose = React.useCallback(function () {
71
- if (!isMounted()) {
72
- return noop;
59
+ setStage(function (stage) {
60
+ // If it's OPENING or already OPEN: override and start CLOSING.
61
+ if (stage === Stage.OPENING || stage === Stage.OPEN) {
62
+ return Stage.CLOSING;
63
+ }
64
+
65
+ // If it was in STANDBY preparing for OPENING: override and mark CLOSED.
66
+ if (stage === Stage.STANDBY) {
67
+ return Stage.CLOSED;
68
+ }
69
+
70
+ // If opening is requested while it's UNMOUNTED, CLOSING or already CLOSED: do nothing.
71
+ // The `useLayoutEffect()` hook below will do the rest.
72
+ return stage;
73
+ });
74
+ }, []);
75
+ useNextFrameEffect(function () {
76
+ var cancel = false;
77
+ if (stage === Stage.STANDBY) {
78
+ // If it's in STANDBY (no longer unmounted, prepared for opening): start OPENING.
79
+ setStage(Stage.OPENING);
73
80
  }
74
- if (refs.current.stage === Stage.CLOSING || refs.current.stage === Stage.CLOSED) {
75
- return noop; // Nothing to do
81
+ if (stage === Stage.OPENING) {
82
+ // Allow the transitions to happen and then switch to OPEN.
83
+ delay(refs.current.transitionDuration).then(function () {
84
+ // If the OPENING sequence was not canceled with later calls, mark it OPEN.
85
+ if (!cancel) {
86
+ setStage(Stage.OPEN);
87
+ }
88
+ });
76
89
  }
77
- var cancel = false;
78
- setStage(Stage.CLOSING);
79
- delay(refs.current.transitionDuration).then(function () {
90
+ if (stage === Stage.CLOSING) {
91
+ delay(refs.current.transitionDuration).then(function () {
92
+ // If the CLOSING sequence was not canceled with later calls, mark it CLOSED.
93
+ if (!cancel) {
94
+ setStage(Stage.CLOSED);
95
+ }
96
+ });
97
+ }
98
+ if (stage === Stage.CLOSED) {
80
99
  var _refs$current$onClose, _refs$current;
81
- if (cancel || !isMounted()) {
82
- return;
83
- }
84
- setStage(Stage.CLOSED);
100
+ // Once the CLOSED state is rendered, trigger the onClosed callback and immediately switch to UNMOUNTED
85
101
  (_refs$current$onClose = (_refs$current = refs.current).onClosed) === null || _refs$current$onClose === void 0 || _refs$current$onClose.call(_refs$current);
86
- }).then(function () {
87
- return delay(0);
88
- }).then(function () {
89
- return setStage(Stage.UNMOUNTED);
90
- });
102
+ setStage(Stage.UNMOUNTED);
103
+ }
91
104
  return function () {
92
105
  cancel = true;
93
106
  };
94
- }, []);
107
+ }, [stage]);
95
108
  React.useEffect(function () {
96
109
  if (shouldOpen) {
97
110
  return onOpen();
98
111
  } else {
99
112
  return onClose();
100
113
  }
101
- }, [open]);
114
+ }, [shouldOpen]);
102
115
  if (stage === Stage.UNMOUNTED) {
103
- // Do not render anything in "closed" stage
116
+ // Do not render anything in "unmounted" stage
104
117
  return null;
105
118
  }
106
119
  return render({
107
- stage: stage,
108
120
  isOpen: stage === Stage.OPEN || stage === Stage.OPENING,
109
121
  onClose: onClose
110
122
  });
package/dist/useLatest.js CHANGED
@@ -14,7 +14,9 @@ var React = _react;
14
14
  */
15
15
  function useLatest(value) {
16
16
  var ref = React.useRef(value);
17
- ref.current = value;
17
+ React.useLayoutEffect(function () {
18
+ ref.current = value;
19
+ }, [value]);
18
20
  return ref;
19
21
  }
20
22
  exports.useLatest = useLatest;
@@ -0,0 +1,3 @@
1
+ type CleanupFn = () => void;
2
+ export declare function useNextFrameEffect(callback: () => CleanupFn, deps: unknown[]): void;
3
+ export {};
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ var _react = require("react");
4
+ var useEffect = _react.useEffect;
5
+ var useEffectEvent = _react.useEffectEvent;
6
+ var _lib = require("./lib");
7
+ var noop = _lib.noop;
8
+ function useNextFrameEffect(callback, deps) {
9
+ var stableCallback = useEffectEvent(callback);
10
+ useEffect(function () {
11
+ var cleanup = noop;
12
+ var frame = requestAnimationFrame(function () {
13
+ cleanup = stableCallback();
14
+ });
15
+ return function () {
16
+ cleanup();
17
+ cancelAnimationFrame(frame);
18
+ };
19
+ }, deps);
20
+ }
21
+ exports.useNextFrameEffect = useNextFrameEffect;
@@ -8,7 +8,6 @@ type ConcurrencyMode = "stack" | "replace" | "ignore";
8
8
  type RenderFunction<P> = (props: P) => React.ReactElement | null | undefined;
9
9
  interface RenderProps<T> {
10
10
  show: boolean;
11
- stage: "closed" | "opening" | "open" | "closing";
12
11
  onDismiss: () => void;
13
12
  onSubmit: (value: Exclude<T, undefined>) => void;
14
13
  }
@@ -81,10 +81,8 @@ function usePromiseModal(render) {
81
81
  }(function (_ref2) {
82
82
  var _render;
83
83
  var isOpen = _ref2.isOpen,
84
- stage = _ref2.stage,
85
84
  onClose = _ref2.onClose;
86
85
  return (_render = render(_objectSpread(_objectSpread({}, args), {}, {
87
- stage: stage,
88
86
  show: isOpen,
89
87
  onDismiss: function onDismiss() {
90
88
  deferred.resolve(undefined);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e1himself/react-promise-modal",
3
- "version": "2.0.2",
3
+ "version": "3.0.1",
4
4
  "description": "The proper (and easy) way of doing modals in React. With Promises.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -38,8 +38,8 @@
38
38
  },
39
39
  "homepage": "https://github.com/e1himself/react-promise-modal#readme",
40
40
  "dependencies": {
41
- "@types/react": "^18.0.0",
42
- "@types/react-dom": "^18.0.0"
41
+ "@types/react": "^19.0.0",
42
+ "@types/react-dom": "^19.0.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@babel/cli": "^7.26.4",
@@ -50,13 +50,13 @@
50
50
  "babel-plugin-transform-es2015-modules-simple-commonjs": "^0.3.0",
51
51
  "np": "^12.0.1",
52
52
  "prettier": "^3.4.2",
53
- "react": "^18.0.0",
54
- "react-dom": "^18.0.0",
53
+ "react": "^19.0.0",
54
+ "react-dom": "^19.0.0",
55
55
  "rimraf": "^6.0.1",
56
56
  "typescript": "^5.7.2"
57
57
  },
58
58
  "peerDependencies": {
59
- "react": ">=18",
60
- "react-dom": ">=18"
59
+ "react": "^19.0.0",
60
+ "react-dom": "^19.0.0"
61
61
  }
62
62
  }