@react-three/fiber 9.2.0 → 9.4.0

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 (28) hide show
  1. package/CHANGELOG.md +1174 -1160
  2. package/dist/declarations/src/core/events.d.ts +92 -92
  3. package/dist/declarations/src/core/hooks.d.ts +53 -53
  4. package/dist/declarations/src/core/index.d.ts +13 -13
  5. package/dist/declarations/src/core/loop.d.ts +31 -31
  6. package/dist/declarations/src/core/reconciler.d.ts +50 -50
  7. package/dist/declarations/src/core/renderer.d.ts +89 -89
  8. package/dist/declarations/src/core/store.d.ts +130 -130
  9. package/dist/declarations/src/core/utils.d.ts +191 -191
  10. package/dist/declarations/src/index.d.ts +6 -6
  11. package/dist/declarations/src/native/Canvas.d.ts +13 -13
  12. package/dist/declarations/src/native/events.d.ts +4 -4
  13. package/dist/declarations/src/native.d.ts +6 -6
  14. package/dist/declarations/src/three-types.d.ts +68 -68
  15. package/dist/declarations/src/web/Canvas.d.ts +23 -23
  16. package/dist/declarations/src/web/events.d.ts +4 -4
  17. package/dist/{events-59fae97e.cjs.prod.js → events-3afec6fc.cjs.prod.js} +128 -101
  18. package/dist/{events-e63d28a3.cjs.dev.js → events-6ab58ae5.cjs.dev.js} +128 -101
  19. package/dist/{events-cf57b220.esm.js → events-f8cd670d.esm.js} +127 -100
  20. package/dist/react-three-fiber.cjs.dev.js +5 -5
  21. package/dist/react-three-fiber.cjs.prod.js +5 -5
  22. package/dist/react-three-fiber.esm.js +6 -6
  23. package/native/dist/react-three-fiber-native.cjs.dev.js +8 -6
  24. package/native/dist/react-three-fiber-native.cjs.prod.js +8 -6
  25. package/native/dist/react-three-fiber-native.esm.js +9 -7
  26. package/native/package.json +5 -5
  27. package/package.json +2 -2
  28. package/readme.md +253 -253
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var THREE = require('three');
4
3
  var React = require('react');
5
4
  var constants = require('react-reconciler/constants');
5
+ var THREE = require('three');
6
6
  var traditional = require('zustand/traditional');
7
+ var suspendReact = require('suspend-react');
7
8
  var Reconciler = require('react-reconciler');
8
9
  var scheduler = require('scheduler');
9
- var suspendReact = require('suspend-react');
10
10
  var jsxRuntime = require('react/jsx-runtime');
11
11
  var itsFine = require('its-fine');
12
12
 
@@ -30,25 +30,25 @@ function _interopNamespace(e) {
30
30
  return Object.freeze(n);
31
31
  }
32
32
 
33
- var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
34
33
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
34
+ var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
35
35
  var Reconciler__default = /*#__PURE__*/_interopDefault(Reconciler);
36
36
 
37
37
  var threeTypes = /*#__PURE__*/Object.freeze({
38
38
  __proto__: null
39
39
  });
40
40
 
41
- /**
42
- * Returns the instance's initial (outmost) root.
41
+ /**
42
+ * Returns the instance's initial (outmost) root.
43
43
  */
44
44
  function findInitialRoot(instance) {
45
45
  let root = instance.root;
46
46
  while (root.getState().previousRoot) root = root.getState().previousRoot;
47
47
  return root;
48
48
  }
49
- /**
50
- * Safely flush async effects when testing, simulating a legacy root.
51
- * @deprecated Import from React instead. import { act } from 'react'
49
+ /**
50
+ * Safely flush async effects when testing, simulating a legacy root.
51
+ * @deprecated Import from React instead. import { act } from 'react'
52
52
  */
53
53
  // Reference with computed key to break Webpack static analysis
54
54
  // https://github.com/webpack/webpack/issues/14814
@@ -57,14 +57,14 @@ const isOrthographicCamera = def => def && def.isOrthographicCamera;
57
57
  const isRef = obj => obj && obj.hasOwnProperty('current');
58
58
  const isColorRepresentation = value => value != null && (typeof value === 'string' || typeof value === 'number' || value.isColor);
59
59
 
60
- /**
61
- * An SSR-friendly useLayoutEffect.
62
- *
63
- * React currently throws a warning when using useLayoutEffect on the server.
64
- * To get around it, we can conditionally useEffect on the server (no-op) and
65
- * useLayoutEffect elsewhere.
66
- *
67
- * @see https://github.com/facebook/react/issues/14927
60
+ /**
61
+ * An SSR-friendly useLayoutEffect.
62
+ *
63
+ * React currently throws a warning when using useLayoutEffect on the server.
64
+ * To get around it, we can conditionally useEffect on the server (no-op) and
65
+ * useLayoutEffect elsewhere.
66
+ *
67
+ * @see https://github.com/facebook/react/issues/14927
68
68
  */
69
69
  const useIsomorphicLayoutEffect = /* @__PURE__ */((_window$document, _window$navigator) => typeof window !== 'undefined' && (((_window$document = window.document) == null ? void 0 : _window$document.createElement) || ((_window$navigator = window.navigator) == null ? void 0 : _window$navigator.product) === 'ReactNative'))() ? React__namespace.useLayoutEffect : React__namespace.useEffect;
70
70
  function useMutableCallback(fn) {
@@ -72,8 +72,8 @@ function useMutableCallback(fn) {
72
72
  useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
73
73
  return ref;
74
74
  }
75
- /**
76
- * Bridges renderer Context and StrictMode from a primary renderer.
75
+ /**
76
+ * Bridges renderer Context and StrictMode from a primary renderer.
77
77
  */
78
78
  function useBridge() {
79
79
  const fiber = itsFine.useFiber();
@@ -125,8 +125,8 @@ function calculateDpr(dpr) {
125
125
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
126
126
  }
127
127
 
128
- /**
129
- * Returns instance root state
128
+ /**
129
+ * Returns instance root state
130
130
  */
131
131
  function getRootState(obj) {
132
132
  var _r3f;
@@ -241,24 +241,46 @@ function prepare(target, root, type, props) {
241
241
  return instance;
242
242
  }
243
243
  function resolve(root, key) {
244
- let target = root[key];
245
244
  if (!key.includes('-')) return {
246
245
  root,
247
246
  key,
248
- target
247
+ target: root[key]
249
248
  };
250
249
 
251
- // Resolve pierced target
252
- target = root;
253
- for (const part of key.split('-')) {
254
- var _target;
250
+ // First try the entire key as a single property (e.g., 'foo-bar')
251
+ if (key in root) {
252
+ return {
253
+ root,
254
+ key,
255
+ target: root[key]
256
+ };
257
+ }
258
+
259
+ // Try piercing (e.g., 'material-color' -> material.color)
260
+ let target = root;
261
+ const parts = key.split('-');
262
+ for (const part of parts) {
263
+ if (typeof target !== 'object' || target === null) {
264
+ if (target !== undefined) {
265
+ // Property exists but has unexpected shape
266
+ const remaining = parts.slice(parts.indexOf(part)).join('-');
267
+ return {
268
+ root: target,
269
+ key: remaining,
270
+ target: undefined
271
+ };
272
+ }
273
+ // Property doesn't exist - fallback to original key
274
+ return {
275
+ root,
276
+ key,
277
+ target: undefined
278
+ };
279
+ }
255
280
  key = part;
256
281
  root = target;
257
- target = (_target = target) == null ? void 0 : _target[key];
282
+ target = target[key];
258
283
  }
259
-
260
- // TODO: change key to 'foo-bar' if target is undefined?
261
-
262
284
  return {
263
285
  root,
264
286
  key,
@@ -401,6 +423,11 @@ function applyProps(object, props) {
401
423
  target
402
424
  } = resolve(object, prop);
403
425
 
426
+ // Throw an error if we attempted to set a pierced prop to a non-object
427
+ if (target === undefined && (typeof root !== 'object' || root === null)) {
428
+ throw Error(`R3F: Cannot set "${prop}". Ensure it is an object before setting "${key}".`);
429
+ }
430
+
404
431
  // Layers must be written to the mask property
405
432
  if (target instanceof THREE__namespace.Layers && value instanceof THREE__namespace.Layers) {
406
433
  target.mask = value.mask;
@@ -489,9 +516,9 @@ function makeId(event) {
489
516
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
490
517
  }
491
518
 
492
- /**
493
- * Release pointer captures.
494
- * This is called by releasePointerCapture in the API, and when an object is removed.
519
+ /**
520
+ * Release pointer captures.
521
+ * This is called by releasePointerCapture in the API, and when an object is removed.
495
522
  */
496
523
  function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
497
524
  const captureData = captures.get(obj);
@@ -838,19 +865,19 @@ function createEvents(store) {
838
865
  if (!(instance != null && instance.eventCount)) return;
839
866
  const handlers = instance.handlers;
840
867
 
841
- /*
842
- MAYBE TODO, DELETE IF NOT:
843
- Check if the object is captured, captured events should not have intersects running in parallel
844
- But wouldn't it be better to just replace capturedMap with a single entry?
845
- Also, are we OK with straight up making picking up multiple objects impossible?
846
-
847
- const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
848
- if (pointerId !== undefined) {
849
- const capturedMeshSet = internal.capturedMap.get(pointerId)
850
- if (capturedMeshSet) {
851
- const captured = capturedMeshSet.get(eventObject)
852
- if (captured && captured.localState.stopped) return
853
- }
868
+ /*
869
+ MAYBE TODO, DELETE IF NOT:
870
+ Check if the object is captured, captured events should not have intersects running in parallel
871
+ But wouldn't it be better to just replace capturedMap with a single entry?
872
+ Also, are we OK with straight up making picking up multiple objects impossible?
873
+
874
+ const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
875
+ if (pointerId !== undefined) {
876
+ const capturedMeshSet = internal.capturedMap.get(pointerId)
877
+ if (capturedMeshSet) {
878
+ const captured = capturedMeshSet.get(eventObject)
879
+ if (captured && captured.localState.stopped) return
880
+ }
854
881
  }*/
855
882
 
856
883
  if (isPointerMove) {
@@ -1141,11 +1168,11 @@ const createStore = (invalidate, advance) => {
1141
1168
  return rootStore;
1142
1169
  };
1143
1170
 
1144
- /**
1145
- * Exposes an object's {@link Instance}.
1146
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useInstanceHandle
1147
- *
1148
- * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1171
+ /**
1172
+ * Exposes an object's {@link Instance}.
1173
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useInstanceHandle
1174
+ *
1175
+ * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1149
1176
  */
1150
1177
  function useInstanceHandle(ref) {
1151
1178
  const instance = React__namespace.useRef(null);
@@ -1153,9 +1180,9 @@ function useInstanceHandle(ref) {
1153
1180
  return instance;
1154
1181
  }
1155
1182
 
1156
- /**
1157
- * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
1158
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
1183
+ /**
1184
+ * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
1185
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
1159
1186
  */
1160
1187
  function useStore() {
1161
1188
  const store = React__namespace.useContext(context);
@@ -1163,18 +1190,18 @@ function useStore() {
1163
1190
  return store;
1164
1191
  }
1165
1192
 
1166
- /**
1167
- * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1168
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1193
+ /**
1194
+ * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1195
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1169
1196
  */
1170
1197
  function useThree(selector = state => state, equalityFn) {
1171
1198
  return useStore()(selector, equalityFn);
1172
1199
  }
1173
1200
 
1174
- /**
1175
- * Executes a callback before render in a shared frame loop.
1176
- * Can order effects with render priority or manually render with a positive priority.
1177
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1201
+ /**
1202
+ * Executes a callback before render in a shared frame loop.
1203
+ * Can order effects with render priority or manually render with a positive priority.
1204
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1178
1205
  */
1179
1206
  function useFrame(callback, renderPriority = 0) {
1180
1207
  const store = useStore();
@@ -1186,9 +1213,9 @@ function useFrame(callback, renderPriority = 0) {
1186
1213
  return null;
1187
1214
  }
1188
1215
 
1189
- /**
1190
- * Returns a node graph of an object with named nodes & materials.
1191
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1216
+ /**
1217
+ * Returns a node graph of an object with named nodes & materials.
1218
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1192
1219
  */
1193
1220
  function useGraph(object) {
1194
1221
  return React__namespace.useMemo(() => buildGraph(object), [object]);
@@ -1224,11 +1251,11 @@ function loadingFn(extensions, onProgress) {
1224
1251
  };
1225
1252
  }
1226
1253
 
1227
- /**
1228
- * Synchronously loads and caches assets with a three loader.
1229
- *
1230
- * Note: this hook's caller must be wrapped with `React.Suspense`
1231
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1254
+ /**
1255
+ * Synchronously loads and caches assets with a three loader.
1256
+ *
1257
+ * Note: this hook's caller must be wrapped with `React.Suspense`
1258
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1232
1259
  */
1233
1260
  function useLoader(loader, input, extensions, onProgress) {
1234
1261
  // Use suspense to load async assets
@@ -1240,25 +1267,22 @@ function useLoader(loader, input, extensions, onProgress) {
1240
1267
  return Array.isArray(input) ? results : results[0];
1241
1268
  }
1242
1269
 
1243
- /**
1244
- * Preloads an asset into cache as a side-effect.
1270
+ /**
1271
+ * Preloads an asset into cache as a side-effect.
1245
1272
  */
1246
1273
  useLoader.preload = function (loader, input, extensions) {
1247
1274
  const keys = Array.isArray(input) ? input : [input];
1248
1275
  return suspendReact.preload(loadingFn(extensions), [loader, ...keys]);
1249
1276
  };
1250
1277
 
1251
- /**
1252
- * Removes a loaded asset from cache.
1278
+ /**
1279
+ * Removes a loaded asset from cache.
1253
1280
  */
1254
1281
  useLoader.clear = function (loader, input) {
1255
1282
  const keys = Array.isArray(input) ? input : [input];
1256
1283
  return suspendReact.clear([loader, ...keys]);
1257
1284
  };
1258
1285
 
1259
- // TODO: upstream to DefinitelyTyped for React 19
1260
- // https://github.com/facebook/react/issues/28956
1261
-
1262
1286
  function createReconciler(config) {
1263
1287
  const reconciler = Reconciler__default["default"](config);
1264
1288
  reconciler.injectIntoDevTools({
@@ -1656,6 +1680,8 @@ const reconciler = /* @__PURE__ */createReconciler({
1656
1680
  suspendInstance() {},
1657
1681
  waitForCommitToBeReady: () => null,
1658
1682
  NotPendingTransition: null,
1683
+ // The reconciler types use the internal ReactContext with all the hidden properties
1684
+ // so we have to cast from the public React.Context type
1659
1685
  HostTransitionContext: /* @__PURE__ */React__namespace.createContext(null),
1660
1686
  setCurrentUpdatePriority(newPriority) {
1661
1687
  currentUpdatePriority = newPriority;
@@ -2083,10 +2109,10 @@ function Portal({
2083
2109
  children,
2084
2110
  container
2085
2111
  }) {
2086
- /** This has to be a component because it would not be able to call useThree/useStore otherwise since
2087
- * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
2088
- * the "R3F hooks can only be used within the Canvas component!" warning:
2089
- * <Canvas>
2112
+ /** This has to be a component because it would not be able to call useThree/useStore otherwise since
2113
+ * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
2114
+ * the "R3F hooks can only be used within the Canvas component!" warning:
2115
+ * <Canvas>
2090
2116
  * {createPortal(...)} */
2091
2117
  const {
2092
2118
  events,
@@ -2167,14 +2193,15 @@ function Portal({
2167
2193
  );
2168
2194
  }
2169
2195
 
2170
- /**
2171
- * Force React to flush any updates inside the provided callback synchronously and immediately.
2172
- * All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync).
2173
- * Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without
2174
- * having to revert to a non-React solution.
2196
+ /**
2197
+ * Force React to flush any updates inside the provided callback synchronously and immediately.
2198
+ * All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync).
2199
+ * Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without
2200
+ * having to revert to a non-React solution. Note: this will only flush updates within the `Canvas` root.
2175
2201
  */
2176
2202
  function flushSync(fn) {
2177
- return reconciler.flushSync(fn);
2203
+ // @ts-ignore - reconciler types are not maintained
2204
+ return reconciler.flushSyncFromReconciler(fn);
2178
2205
  }
2179
2206
 
2180
2207
  function createSubs(callback, subs) {
@@ -2188,21 +2215,21 @@ const globalEffects = new Set();
2188
2215
  const globalAfterEffects = new Set();
2189
2216
  const globalTailEffects = new Set();
2190
2217
 
2191
- /**
2192
- * Adds a global render callback which is called each frame.
2193
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
2218
+ /**
2219
+ * Adds a global render callback which is called each frame.
2220
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
2194
2221
  */
2195
2222
  const addEffect = callback => createSubs(callback, globalEffects);
2196
2223
 
2197
- /**
2198
- * Adds a global after-render callback which is called each frame.
2199
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
2224
+ /**
2225
+ * Adds a global after-render callback which is called each frame.
2226
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
2200
2227
  */
2201
2228
  const addAfterEffect = callback => createSubs(callback, globalAfterEffects);
2202
2229
 
2203
- /**
2204
- * Adds a global callback which is called when rendering stops.
2205
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
2230
+ /**
2231
+ * Adds a global callback which is called when rendering stops.
2232
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
2206
2233
  */
2207
2234
  const addTail = callback => createSubs(callback, globalTailEffects);
2208
2235
  function run(effects, timestamp) {
@@ -2290,9 +2317,9 @@ function loop(timestamp) {
2290
2317
  }
2291
2318
  }
2292
2319
 
2293
- /**
2294
- * Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
2295
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
2320
+ /**
2321
+ * Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
2322
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
2296
2323
  */
2297
2324
  function invalidate(state, frames = 1) {
2298
2325
  var _state$gl$xr2;
@@ -2319,9 +2346,9 @@ function invalidate(state, frames = 1) {
2319
2346
  }
2320
2347
  }
2321
2348
 
2322
- /**
2323
- * Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
2324
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
2349
+ /**
2350
+ * Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
2351
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
2325
2352
  */
2326
2353
  function advance(timestamp, runGlobalEffects = true, state, frame) {
2327
2354
  if (runGlobalEffects) flushGlobalEffects('before', timestamp);