@ripple-ts/compat-react 0.2.164 → 0.2.165

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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/index.js +49 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ripple-ts/compat-react",
3
- "version": "0.2.164",
3
+ "version": "0.2.165",
4
4
  "description": "Ripple compatibility layer for React",
5
5
  "main": "src/index.js",
6
6
  "author": "Dominic Gannaway",
@@ -17,7 +17,7 @@
17
17
  "dependencies": {
18
18
  "react": "^19.2.0",
19
19
  "react-dom": "^19.2.0",
20
- "ripple": "0.2.164"
20
+ "ripple": "0.2.165"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/react": "^19.2.2",
package/src/index.js CHANGED
@@ -2,7 +2,14 @@
2
2
  /** @import { ReactNode } from 'react' */
3
3
 
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { useSyncExternalStore, useLayoutEffect, useRef, useState, Component } from 'react';
5
+ import {
6
+ useSyncExternalStore,
7
+ useLayoutEffect,
8
+ useRef,
9
+ useState,
10
+ Component,
11
+ Suspense,
12
+ } from 'react';
6
13
  import { createPortal } from 'react-dom';
7
14
  import { createRoot } from 'react-dom/client';
8
15
  import {
@@ -14,6 +21,9 @@ import {
14
21
  tracked,
15
22
  get_tracked,
16
23
  handle_error,
24
+ suspend,
25
+ TRY_BLOCK,
26
+ destroy_block,
17
27
  } from 'ripple/internal/client';
18
28
  import { Context } from 'ripple';
19
29
 
@@ -36,6 +46,22 @@ function map_portals(portals) {
36
46
  });
37
47
  }
38
48
 
49
+ /**
50
+ * @param {any} block
51
+ * @returns {boolean}
52
+ */
53
+ function is_inside_try_pending(block) {
54
+ let current = block;
55
+
56
+ while (current) {
57
+ if (current.f & TRY_BLOCK && current.s.a !== null) {
58
+ return true;
59
+ }
60
+ current = current.p;
61
+ }
62
+ return false;
63
+ }
64
+
39
65
  export function createReactCompat() {
40
66
  const root_portals = new Map();
41
67
  /** @type {{ portals: Map<any, any>, update: Function}} */
@@ -76,17 +102,30 @@ export function createReactCompat() {
76
102
  };
77
103
  }
78
104
 
105
+ const use_suspense = is_inside_try_pending(e);
106
+
79
107
  function ReactCompat() {
80
108
  return useSyncExternalStore(subscribe, () => react_node);
81
109
  }
82
110
 
111
+ function SuspenseHandler() {
112
+ useLayoutEffect(() => {
113
+ return with_block(e, () => suspend());
114
+ }, []);
115
+
116
+ return null;
117
+ }
118
+
83
119
  class ReactCompatBoundary extends Component {
84
120
  state = { e: false };
85
121
 
86
- static getDerivedStateFromError(error) {
122
+ static getDerivedStateFromError() {
87
123
  return { e: true };
88
124
  }
89
125
 
126
+ /**
127
+ * @param {unknown} error
128
+ */
90
129
  componentDidCatch(error) {
91
130
  handle_error(error, e);
92
131
  }
@@ -95,6 +134,12 @@ export function createReactCompat() {
95
134
  if (this.state?.e) {
96
135
  return null;
97
136
  }
137
+ if (use_suspense) {
138
+ return jsx(Suspense, {
139
+ fallback: jsx(SuspenseHandler, {}),
140
+ children: jsx(ReactCompat, {}),
141
+ });
142
+ }
98
143
  return jsx(ReactCompat, {});
99
144
  }
100
145
  }
@@ -169,6 +214,7 @@ export function Ripple({ component, props }) {
169
214
  const proxied_props = proxy_props(() => get_tracked(tracked_props));
170
215
  frag.append(anchor);
171
216
 
217
+ /** @type {any} */
172
218
  const b = with_block(block, () => {
173
219
  PortalContext.set({ portals, update });
174
220
  return branch(() => {
@@ -180,6 +226,7 @@ export function Ripple({ component, props }) {
180
226
 
181
227
  return () => {
182
228
  anchor.remove();
229
+ destroy_block(b);
183
230
  };
184
231
  }, [component]);
185
232