@react-three/fiber 8.0.7 → 8.0.10

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.
package/CHANGELOG.md CHANGED
@@ -1,10 +1,28 @@
1
1
  # @react-three/fiber
2
2
 
3
+ ## 8.0.10
4
+
5
+ ### Patch Changes
6
+
7
+ - eb321afd: fix: remount bug, allow portals to inject custom size
8
+
9
+ ## 8.0.9
10
+
11
+ ### Patch Changes
12
+
13
+ - 624df949: fix: canvas unmount race condition"
14
+
15
+ ## 8.0.8
16
+
17
+ ### Patch Changes
18
+
19
+ - 952a566: fix: react SSR
20
+
3
21
  ## 8.0.7
4
22
 
5
23
  ### Patch Changes
6
24
 
7
- - 01ceb7e: remove undefined keys on detach instead of letting them hang
25
+ - f63806b: fix: react SSR
8
26
 
9
27
  ## 8.0.6
10
28
 
@@ -10,16 +10,12 @@ export declare type Extensions = (loader: THREE.Loader) => void;
10
10
  export declare type LoaderResult<T> = T extends any[] ? Loader<T[number]> : Loader<T>;
11
11
  export declare type ConditionalType<Child, Parent, Truthy, Falsy> = Child extends Parent ? Truthy : Falsy;
12
12
  export declare type BranchingReturn<T, Parent, Coerced> = ConditionalType<T, Parent, Coerced, T>;
13
- declare type noop = (...args: any[]) => any;
14
- declare type PickFunction<T extends noop> = (...args: Parameters<T>) => ReturnType<T>;
15
13
  export declare function useStore(): import("zustand").UseBoundStore<RootState, import("zustand").StoreApi<RootState>>;
16
14
  export declare function useThree<T = RootState>(selector?: StateSelector<RootState, T>, equalityFn?: EqualityChecker<T>): T;
17
15
  export declare function useFrame(callback: RenderCallback, renderPriority?: number): null;
18
16
  export declare function useGraph(object: THREE.Object3D): ObjectMap;
19
- export declare function useMemoizedFn<T extends noop>(fn?: T): PickFunction<T>;
20
17
  export declare function useLoader<T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U, extensions?: Extensions, onProgress?: (event: ProgressEvent<EventTarget>) => void): U extends any[] ? BranchingReturn<T, GLTF, GLTF & ObjectMap>[] : BranchingReturn<T, GLTF, GLTF & ObjectMap>;
21
18
  export declare namespace useLoader {
22
19
  var preload: <T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U, extensions?: Extensions | undefined) => undefined;
23
20
  var clear: <T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U) => void;
24
21
  }
25
- export {};
@@ -2,11 +2,12 @@
2
2
  import * as THREE from 'three';
3
3
  import * as React from 'react';
4
4
  import { UseBoundStore } from 'zustand';
5
- import { Renderer, StoreProps, context, RootState, Size } from './store';
5
+ import * as ReactThreeFiber from '../three-types';
6
+ import { Renderer, context, RootState, Size, Dpr, Performance } from './store';
6
7
  import { extend, Root } from './renderer';
7
8
  import { addEffect, addAfterEffect, addTail } from './loop';
8
9
  import { EventManager, ComputeFunction } from './events';
9
- import { dispose, getRootState } from './utils';
10
+ import { dispose, getRootState, Camera } from './utils';
10
11
  declare const roots: Map<Element, Root>;
11
12
  declare const invalidate: (state?: RootState | undefined) => void, advance: (timestamp: number, runGlobalEffects?: boolean, state?: RootState | undefined, frame?: THREE.XRFrame | undefined) => void;
12
13
  declare const reconciler: import("react-reconciler").Reconciler<unknown, unknown, unknown, unknown, unknown>, applyProps: typeof import("./utils").applyProps;
@@ -14,11 +15,24 @@ declare type Properties<T> = Pick<T, {
14
15
  [K in keyof T]: T[K] extends (_: any) => any ? never : K;
15
16
  }[keyof T]>;
16
17
  declare type GLProps = Renderer | ((canvas: HTMLCanvasElement) => Renderer) | Partial<Properties<THREE.WebGLRenderer> | THREE.WebGLRendererParameters> | undefined;
17
- export declare type RenderProps<TCanvas extends Element> = Omit<StoreProps, 'gl' | 'events' | 'size'> & {
18
+ export declare type RenderProps<TCanvas extends Element> = {
18
19
  gl?: GLProps;
19
- events?: (store: UseBoundStore<RootState>) => EventManager<TCanvas>;
20
20
  size?: Size;
21
+ shadows?: boolean | Partial<THREE.WebGLShadowMap>;
22
+ legacy?: boolean;
23
+ linear?: boolean;
24
+ flat?: boolean;
25
+ orthographic?: boolean;
26
+ frameloop?: 'always' | 'demand' | 'never';
27
+ performance?: Partial<Omit<Performance, 'regress'>>;
28
+ dpr?: Dpr;
29
+ raycaster?: Partial<THREE.Raycaster>;
30
+ camera?: (Camera | Partial<ReactThreeFiber.Object3DNode<THREE.Camera, typeof THREE.Camera> & ReactThreeFiber.Object3DNode<THREE.PerspectiveCamera, typeof THREE.PerspectiveCamera> & ReactThreeFiber.Object3DNode<THREE.OrthographicCamera, typeof THREE.OrthographicCamera>>) & {
31
+ manual?: boolean;
32
+ };
33
+ events?: (store: UseBoundStore<RootState>) => EventManager<HTMLElement>;
21
34
  onCreated?: (state: RootState) => void;
35
+ onPointerMissed?: (event: MouseEvent) => void;
22
36
  };
23
37
  export declare type ReconcilerRoot<TCanvas extends Element> = {
24
38
  configure: (config?: RenderProps<TCanvas>) => ReconcilerRoot<TCanvas>;
@@ -28,13 +42,17 @@ export declare type ReconcilerRoot<TCanvas extends Element> = {
28
42
  declare function createRoot<TCanvas extends Element>(canvas: TCanvas): ReconcilerRoot<TCanvas>;
29
43
  declare function render<TCanvas extends Element>(children: React.ReactNode, canvas: TCanvas, config: RenderProps<TCanvas>): UseBoundStore<RootState>;
30
44
  declare function unmountComponentAtNode<TElement extends Element>(canvas: TElement, callback?: (canvas: TElement) => void): void;
31
- export declare type InjectState = Partial<Omit<RootState, 'set' | 'get' | 'setSize' | 'setFrameloop' | 'setDpr' | 'events' | 'invalidate' | 'advance' | 'performance' | 'internal'> & {
32
- events: {
45
+ export declare type InjectState = Partial<Omit<RootState, 'set' | 'get' | 'setSize' | 'setFrameloop' | 'setDpr' | 'events' | 'invalidate' | 'advance' | 'performance' | 'internal' | 'size' | 'viewport'> & {
46
+ events?: {
33
47
  enabled?: boolean;
34
48
  priority?: number;
35
49
  compute?: ComputeFunction;
36
50
  connected?: any;
37
51
  };
52
+ size?: {
53
+ width: number;
54
+ height: number;
55
+ };
38
56
  }>;
39
57
  declare function createPortal(children: React.ReactNode, container: THREE.Object3D, state?: InjectState): React.ReactNode;
40
58
  declare const act: any;
@@ -1,8 +1,8 @@
1
1
  import * as THREE from 'three';
2
2
  import * as React from 'react';
3
- import * as ReactThreeFiber from '../three-types';
4
3
  import { GetState, SetState, StoreApi, UseBoundStore } from 'zustand';
5
4
  import { DomEvent, EventManager, PointerCaptureTarget, ThreeEvent } from './events';
5
+ import { Camera } from './utils';
6
6
  export interface Intersection extends THREE.Intersection {
7
7
  eventObject: THREE.Object3D;
8
8
  }
@@ -23,7 +23,6 @@ export declare type Viewport = Size & {
23
23
  distance: number;
24
24
  aspect: number;
25
25
  };
26
- export declare type Camera = THREE.OrthographicCamera | THREE.PerspectiveCamera;
27
26
  export declare type RenderCallback = (state: RootState, delta: number, frame?: THREE.XRFrame) => void;
28
27
  export declare type Performance = {
29
28
  current: number;
@@ -36,7 +35,6 @@ export declare type Renderer = {
36
35
  render: (scene: THREE.Scene, camera: THREE.Camera) => any;
37
36
  };
38
37
  export declare const isRenderer: (def: any) => boolean;
39
- export declare const isOrthographicCamera: (def: any) => def is THREE.OrthographicCamera;
40
38
  export declare type InternalState = {
41
39
  active: boolean;
42
40
  priority: number;
@@ -87,23 +85,6 @@ export declare type RootState = {
87
85
  previousRoot?: UseBoundStore<RootState, StoreApi<RootState>>;
88
86
  internal: InternalState;
89
87
  };
90
- export declare type StoreProps = {
91
- gl: THREE.WebGLRenderer;
92
- size: Size;
93
- shadows?: boolean | Partial<THREE.WebGLShadowMap>;
94
- legacy?: boolean;
95
- linear?: boolean;
96
- flat?: boolean;
97
- orthographic?: boolean;
98
- frameloop?: 'always' | 'demand' | 'never';
99
- performance?: Partial<Omit<Performance, 'regress'>>;
100
- dpr?: Dpr;
101
- raycaster?: Partial<THREE.Raycaster>;
102
- camera?: (Camera | Partial<ReactThreeFiber.Object3DNode<THREE.Camera, typeof THREE.Camera> & ReactThreeFiber.Object3DNode<THREE.PerspectiveCamera, typeof THREE.PerspectiveCamera> & ReactThreeFiber.Object3DNode<THREE.OrthographicCamera, typeof THREE.OrthographicCamera>>) & {
103
- manual?: boolean;
104
- };
105
- onPointerMissed?: (event: MouseEvent) => void;
106
- };
107
88
  declare const context: React.Context<UseBoundStore<RootState, StoreApi<RootState>>>;
108
89
  declare const createStore: (invalidate: (state?: RootState | undefined) => void, advance: (timestamp: number, runGlobalEffects?: boolean | undefined, state?: RootState | undefined, frame?: THREE.XRFrame | undefined) => void) => UseBoundStore<RootState>;
109
90
  export { createStore, context };
@@ -1,6 +1,31 @@
1
1
  import * as THREE from 'three';
2
+ import * as React from 'react';
2
3
  import { AttachType, Instance, InstanceProps, LocalState } from './renderer';
3
- import { Dpr, RootState } from './store';
4
+ import { Dpr, RootState, Size } from './store';
5
+ export declare type Camera = THREE.OrthographicCamera | THREE.PerspectiveCamera;
6
+ export declare const isOrthographicCamera: (def: Camera) => def is THREE.OrthographicCamera;
7
+ export declare const useIsomorphicLayoutEffect: typeof React.useEffect;
8
+ export declare function useMutableCallback<T>(fn: T): React.MutableRefObject<T>;
9
+ export declare type SetBlock = false | Promise<null> | null;
10
+ export declare type UnblockProps = {
11
+ set: React.Dispatch<React.SetStateAction<SetBlock>>;
12
+ children: React.ReactNode;
13
+ };
14
+ export declare function Block({ set }: Omit<UnblockProps, 'children'>): null;
15
+ export declare class ErrorBoundary extends React.Component<{
16
+ set: React.Dispatch<any>;
17
+ }, {
18
+ error: boolean;
19
+ }> {
20
+ state: {
21
+ error: boolean;
22
+ };
23
+ static getDerivedStateFromError: () => {
24
+ error: boolean;
25
+ };
26
+ componentDidCatch(error: any): void;
27
+ render(): React.ReactNode;
28
+ }
4
29
  export declare const DEFAULT = "__default";
5
30
  export declare type DiffSet = {
6
31
  memoized: {
@@ -55,3 +80,6 @@ export declare function diffProps(instance: Instance, { children: cN, key: kN, r
55
80
  export declare function applyProps(instance: Instance, data: InstanceProps | DiffSet): Instance;
56
81
  export declare function invalidateInstance(instance: Instance): void;
57
82
  export declare function updateInstance(instance: Instance): void;
83
+ export declare function updateCamera(camera: Camera & {
84
+ manual?: boolean;
85
+ }, size: Size): void;
@@ -2,9 +2,9 @@ export * from './three-types';
2
2
  import * as ReactThreeFiber from './three-types';
3
3
  export { ReactThreeFiber };
4
4
  export type { BaseInstance, LocalState } from './core/renderer';
5
- export type { Intersection, Subscription, Dpr, Size, Viewport, Camera, RenderCallback, Performance, RootState, } from './core/store';
6
- export type { ThreeEvent, Events, EventManager } from './core/events';
7
- export type { ObjectMap } from './core/utils';
5
+ export type { Intersection, Subscription, Dpr, Size, Viewport, RenderCallback, Performance, RootState, } from './core/store';
6
+ export type { ThreeEvent, Events, EventManager, ComputeFunction } from './core/events';
7
+ export type { ObjectMap, Camera } from './core/utils';
8
8
  export * from './web/Canvas';
9
9
  export { createPointerEvents as events } from './web/events';
10
10
  export * from './core';
@@ -1,13 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { View, ViewProps, ViewStyle } from 'react-native';
3
- import { UseBoundStore } from 'zustand';
4
3
  import { RenderProps } from '../core';
5
- import { RootState } from '../core/store';
6
- import { EventManager } from '../core/events';
7
- export interface Props extends Omit<RenderProps<HTMLCanvasElement>, 'size' | 'events'>, ViewProps {
4
+ export interface Props extends Omit<RenderProps<HTMLCanvasElement>, 'size'>, ViewProps {
8
5
  children: React.ReactNode;
9
- fallback?: React.ReactNode;
10
6
  style?: ViewStyle;
11
- events?: (store: UseBoundStore<RootState>) => EventManager<any>;
12
7
  }
13
8
  export declare const Canvas: React.ForwardRefExoticComponent<Props & React.RefAttributes<View>>;
@@ -1,5 +1,4 @@
1
1
  import { UseBoundStore } from 'zustand';
2
2
  import { RootState } from '../core/store';
3
3
  import { EventManager } from '../core/events';
4
- import { View } from 'react-native';
5
- export declare function createTouchEvents(store: UseBoundStore<RootState>): EventManager<View>;
4
+ export declare function createTouchEvents(store: UseBoundStore<RootState>): EventManager<HTMLElement>;
@@ -2,9 +2,9 @@ export * from './three-types';
2
2
  import * as ReactThreeFiber from './three-types';
3
3
  export { ReactThreeFiber };
4
4
  export type { BaseInstance, LocalState } from './core/renderer';
5
- export type { Intersection, Subscription, Dpr, Size, Viewport, Camera, RenderCallback, Performance, RootState, } from './core/store';
6
- export type { ThreeEvent, Events, EventManager } from './core/events';
7
- export type { ObjectMap } from './core/utils';
5
+ export type { Intersection, Subscription, Dpr, Size, Viewport, RenderCallback, Performance, RootState, } from './core/store';
6
+ export type { ThreeEvent, Events, EventManager, ComputeFunction } from './core/events';
7
+ export type { ObjectMap, Camera } from './core/utils';
8
8
  export * from './native/Canvas';
9
9
  export { createTouchEvents as events } from './native/events';
10
10
  export * from './core';
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import * as THREE from 'three';
2
3
  import { EventHandlers } from './core/events';
3
4
  import { AttachType } from './core/renderer';
@@ -1,13 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import type { Options as ResizeOptions } from 'react-use-measure';
3
- import { UseBoundStore } from 'zustand';
4
3
  import { RenderProps } from '../core';
5
- import { RootState } from '../core/store';
6
- import { EventManager } from '../core/events';
7
- export interface Props extends Omit<RenderProps<HTMLCanvasElement>, 'size' | 'events'>, React.HTMLAttributes<HTMLDivElement> {
4
+ export interface Props extends Omit<RenderProps<HTMLCanvasElement>, 'size'>, React.HTMLAttributes<HTMLDivElement> {
8
5
  children: React.ReactNode;
9
6
  fallback?: React.ReactNode;
10
7
  resize?: ResizeOptions;
11
- events?: (store: UseBoundStore<RootState>) => EventManager<any>;
12
8
  }
13
9
  export declare const Canvas: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLCanvasElement>>;
@@ -39,6 +39,48 @@ var threeTypes = /*#__PURE__*/Object.freeze({
39
39
  __proto__: null
40
40
  });
41
41
 
42
+ const isOrthographicCamera = def => def && def.isOrthographicCamera; // React currently throws a warning when using useLayoutEffect on the server.
43
+ // To get around it, we can conditionally useEffect on the server (no-op) and
44
+ // useLayoutEffect on the client.
45
+
46
+ const isSSR = typeof window === 'undefined' || !window.navigator || /ServerSideRendering|^Deno\//.test(window.navigator.userAgent);
47
+ const useIsomorphicLayoutEffect = isSSR ? React__namespace.useEffect : React__namespace.useLayoutEffect;
48
+ function useMutableCallback(fn) {
49
+ const ref = React__namespace.useRef(fn);
50
+ useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
51
+ return ref;
52
+ }
53
+ function Block({
54
+ set
55
+ }) {
56
+ useIsomorphicLayoutEffect(() => {
57
+ set(new Promise(() => null));
58
+ return () => set(false);
59
+ }, [set]);
60
+ return null;
61
+ }
62
+ class ErrorBoundary extends React__namespace.Component {
63
+ constructor(...args) {
64
+ super(...args);
65
+ this.state = {
66
+ error: false
67
+ };
68
+ }
69
+
70
+ componentDidCatch(error) {
71
+ this.props.set(error);
72
+ }
73
+
74
+ render() {
75
+ return this.state.error ? null : this.props.children;
76
+ }
77
+
78
+ }
79
+
80
+ ErrorBoundary.getDerivedStateFromError = () => ({
81
+ error: true
82
+ });
83
+
42
84
  const DEFAULT = '__default';
43
85
  const isDiffSet = def => def && !!def.memoized && !!def.changes;
44
86
  function calculateDpr(dpr) {
@@ -211,10 +253,7 @@ function detach(parent, child, type) {
211
253
  target,
212
254
  key
213
255
  } = resolve(parent, type);
214
- const previous = child.__r3f.previousAttach; // When the previous value was undefined, it means the value was never set to begin with
215
-
216
- if (previous === undefined) delete target[key]; // Otherwise set the previous value
217
- else target[key] = previous;
256
+ target[key] = child.__r3f.previousAttach;
218
257
  } else (_child$__r3f = child.__r3f) == null ? void 0 : _child$__r3f.previousAttach == null ? void 0 : _child$__r3f.previousAttach(parent, child);
219
258
 
220
259
  (_child$__r3f2 = child.__r3f) == null ? true : delete _child$__r3f2.previousAttach;
@@ -307,11 +346,9 @@ function applyProps$1(instance, data) {
307
346
  // use the prop constructor to find the default it should be
308
347
  value = new targetProp.constructor(...memoized.args);
309
348
  } else if (currentInstance.constructor) {
310
- var _currentInstance$__r, _currentInstance$__r2;
311
-
312
349
  // create a blank slate of the instance and copy the particular parameter.
313
350
  // @ts-ignore
314
- const defaultClassCall = new currentInstance.constructor(...((_currentInstance$__r = (_currentInstance$__r2 = currentInstance.__r3f) == null ? void 0 : _currentInstance$__r2.memoizedProps.args) != null ? _currentInstance$__r : []));
351
+ const defaultClassCall = new currentInstance.constructor(...currentInstance.__r3f.memoizedProps.args);
315
352
  value = defaultClassCall[targetProp]; // destory the instance
316
353
 
317
354
  if (defaultClassCall.dispose) defaultClassCall.dispose(); // instance does not have constructor, just set it to 0
@@ -380,6 +417,25 @@ function invalidateInstance(instance) {
380
417
  function updateInstance(instance) {
381
418
  instance.onUpdate == null ? void 0 : instance.onUpdate(instance);
382
419
  }
420
+ function updateCamera(camera, size) {
421
+ // https://github.com/pmndrs/react-three-fiber/issues/92
422
+ // Do not mess with the camera if it belongs to the user
423
+ if (!camera.manual) {
424
+ if (isOrthographicCamera(camera)) {
425
+ camera.left = size.width / -2;
426
+ camera.right = size.width / 2;
427
+ camera.top = size.height / 2;
428
+ camera.bottom = size.height / -2;
429
+ } else {
430
+ camera.aspect = size.width / size.height;
431
+ }
432
+
433
+ camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
434
+ // Update matrix world since the renderer is a frame late
435
+
436
+ camera.updateMatrixWorld();
437
+ }
438
+ }
383
439
 
384
440
  function makeId(event) {
385
441
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
@@ -1153,7 +1209,6 @@ function createRenderer(roots, getEventPriority) {
1153
1209
  }
1154
1210
 
1155
1211
  const isRenderer = def => !!(def != null && def.render);
1156
- const isOrthographicCamera = def => def && def.isOrthographicCamera;
1157
1212
  const context = /*#__PURE__*/React__namespace.createContext(null);
1158
1213
 
1159
1214
  const createStore = (invalidate, advance) => {
@@ -1360,24 +1415,7 @@ const createStore = (invalidate, advance) => {
1360
1415
  } = rootState.getState();
1361
1416
 
1362
1417
  if (size !== oldSize || viewport.dpr !== oldDpr) {
1363
- // https://github.com/pmndrs/react-three-fiber/issues/92
1364
- // Do not mess with the camera if it belongs to the user
1365
- if (!camera.manual) {
1366
- if (isOrthographicCamera(camera)) {
1367
- camera.left = size.width / -2;
1368
- camera.right = size.width / 2;
1369
- camera.top = size.height / 2;
1370
- camera.bottom = size.height / -2;
1371
- } else {
1372
- camera.aspect = size.width / size.height;
1373
- }
1374
-
1375
- camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1376
- // Update matrix world since the renderer is a frame late
1377
-
1378
- camera.updateMatrixWorld();
1379
- } // Update renderer
1380
-
1418
+ updateCamera(camera, size); // Update renderer
1381
1419
 
1382
1420
  gl.setPixelRatio(viewport.dpr);
1383
1421
  gl.setSize(size.width, size.height);
@@ -1401,8 +1439,23 @@ let i;
1401
1439
  let globalEffects = [];
1402
1440
  let globalAfterEffects = [];
1403
1441
  let globalTailEffects = [];
1442
+ /**
1443
+ * Adds a global render callback which is called each frame.
1444
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
1445
+ */
1446
+
1404
1447
  const addEffect = callback => createSubs(callback, globalEffects);
1448
+ /**
1449
+ * Adds a global after-render callback which is called each frame.
1450
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
1451
+ */
1452
+
1405
1453
  const addAfterEffect = callback => createSubs(callback, globalAfterEffects);
1454
+ /**
1455
+ * Adds a global callback which is called when rendering stops.
1456
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
1457
+ */
1458
+
1406
1459
  const addTail = callback => createSubs(callback, globalTailEffects);
1407
1460
 
1408
1461
  function run(effects, timestamp) {
@@ -1411,11 +1464,10 @@ function run(effects, timestamp) {
1411
1464
 
1412
1465
  let subscribers;
1413
1466
  let subscription;
1414
- let delta;
1415
1467
 
1416
1468
  function render$1(timestamp, state, frame) {
1417
1469
  // Run local effects
1418
- delta = state.clock.getDelta(); // In frameloop='never' mode, clock times are updated using the provided timestamp
1470
+ let delta = state.clock.getDelta(); // In frameloop='never' mode, clock times are updated using the provided timestamp
1419
1471
 
1420
1472
  if (state.frameloop === 'never' && typeof timestamp === 'number') {
1421
1473
  delta = timestamp - state.clock.elapsedTime;
@@ -1494,7 +1546,17 @@ function createLoop(roots) {
1494
1546
 
1495
1547
  return {
1496
1548
  loop,
1549
+
1550
+ /**
1551
+ * Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
1552
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
1553
+ */
1497
1554
  invalidate,
1555
+
1556
+ /**
1557
+ * Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
1558
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
1559
+ */
1498
1560
  advance
1499
1561
  };
1500
1562
  }
@@ -1504,19 +1566,34 @@ function useStore() {
1504
1566
  if (!store) throw `R3F hooks can only be used within the Canvas component!`;
1505
1567
  return store;
1506
1568
  }
1569
+ /**
1570
+ * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1571
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1572
+ */
1573
+
1507
1574
  function useThree(selector = state => state, equalityFn) {
1508
1575
  return useStore()(selector, equalityFn);
1509
1576
  }
1577
+ /**
1578
+ * Executes a callback before render in a shared frame loop.
1579
+ * Can order effects with render priority or manually render with a positive priority.
1580
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1581
+ */
1582
+
1510
1583
  function useFrame(callback, renderPriority = 0) {
1511
1584
  const store = useStore();
1512
- const subscribe = store.getState().internal.subscribe; // Update ref
1585
+ const subscribe = store.getState().internal.subscribe; // Memoize ref
1513
1586
 
1514
- const ref = React__namespace.useRef(callback);
1515
- React__namespace.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe on mount, unsubscribe on unmount
1587
+ const ref = useMutableCallback(callback); // Subscribe on mount, unsubscribe on unmount
1516
1588
 
1517
- React__namespace.useLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1589
+ useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1518
1590
  return null;
1519
1591
  }
1592
+ /**
1593
+ * Returns a node graph of an object with named nodes & materials.
1594
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1595
+ */
1596
+
1520
1597
  function useGraph(object) {
1521
1598
  return React__namespace.useMemo(() => buildGraph(object), [object]);
1522
1599
  }
@@ -1533,12 +1610,14 @@ function loadingFn(extensions, onProgress) {
1533
1610
  }, onProgress, error => reject(`Could not load ${input}: ${error.message}`)))));
1534
1611
  };
1535
1612
  }
1613
+ /**
1614
+ * Synchronously loads and caches assets with a three loader.
1615
+ *
1616
+ * Note: this hook's caller must be wrapped with `React.Suspense`
1617
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1618
+ */
1619
+
1536
1620
 
1537
- function useMemoizedFn(fn) {
1538
- const fnRef = React__namespace.useRef(fn);
1539
- React__namespace.useLayoutEffect(() => void (fnRef.current = fn), [fn]);
1540
- return (...args) => fnRef.current == null ? void 0 : fnRef.current(...args);
1541
- }
1542
1621
  function useLoader(Proto, input, extensions, onProgress) {
1543
1622
  // Use suspense to load async assets
1544
1623
  const keys = Array.isArray(input) ? input : [input];
@@ -1548,11 +1627,18 @@ function useLoader(Proto, input, extensions, onProgress) {
1548
1627
 
1549
1628
  return Array.isArray(input) ? results : results[0];
1550
1629
  }
1630
+ /**
1631
+ * Preloads an asset into cache as a side-effect.
1632
+ */
1551
1633
 
1552
1634
  useLoader.preload = function (Proto, input, extensions) {
1553
1635
  const keys = Array.isArray(input) ? input : [input];
1554
1636
  return suspendReact.preload(loadingFn(extensions), [Proto, ...keys]);
1555
1637
  };
1638
+ /**
1639
+ * Removes a loaded asset from cache.
1640
+ */
1641
+
1556
1642
 
1557
1643
  useLoader.clear = function (Proto, input) {
1558
1644
  const keys = Array.isArray(input) ? input : [input];
@@ -1799,7 +1885,7 @@ function Provider({
1799
1885
  onCreated,
1800
1886
  rootElement
1801
1887
  }) {
1802
- React__namespace.useLayoutEffect(() => {
1888
+ useIsomorphicLayoutEffect(() => {
1803
1889
  const state = store.getState(); // Flag the canvas active, rendering will now begin
1804
1890
 
1805
1891
  state.set(state => ({
@@ -1868,6 +1954,7 @@ function Portal({
1868
1954
  * {createPortal(...)} */
1869
1955
  const {
1870
1956
  events,
1957
+ size,
1871
1958
  ...rest
1872
1959
  } = state;
1873
1960
  const previousRoot = useStore();
@@ -1879,45 +1966,79 @@ function Portal({
1879
1966
 
1880
1967
  if (injectState) {
1881
1968
  // Only the fields of "state" that do not differ from injectState
1969
+ // Some props should be off-limits
1970
+ // Otherwise filter out the props that are different and let the inject layer take precedence
1882
1971
  Object.keys(state).forEach(key => {
1883
- if ( // Some props should be off-limits
1884
- !['size', 'viewport', 'internal', 'performance'].includes(key) && // Otherwise filter out the props that are different and let the inject layer take precedence
1885
- state[key] !== injectState[key]) delete intersect[key];
1972
+ if (state[key] !== injectState[key] && !['internal', 'performance'].includes(key)) {
1973
+ delete intersect[key];
1974
+ }
1886
1975
  });
1887
1976
  }
1888
1977
 
1889
- return { ...intersect,
1978
+ let viewport = undefined;
1979
+
1980
+ if (injectState && size) {
1981
+ const camera = injectState.camera; // Calculate the override viewport, if present
1982
+
1983
+ viewport = state.viewport.getCurrentViewport(camera, new THREE__namespace.Vector3(), size); // Update the portal camera, if it differs from the previous layer
1984
+
1985
+ if (camera !== state.camera) updateCamera(camera, size);
1986
+ }
1987
+
1988
+ return { // The intersect consists of the previous root state
1989
+ ...intersect,
1990
+ // Portals have their own scene, which forms the root, a raycaster and a pointer
1890
1991
  scene: container,
1891
- previousRoot,
1892
1992
  raycaster,
1993
+ pointer,
1994
+ mouse: pointer,
1995
+ // Their previous root is the layer before it
1996
+ previousRoot,
1997
+ // Events, size and viewport can be overridden by the inject layer
1893
1998
  events: { ...state.events,
1894
1999
  ...(injectState == null ? void 0 : injectState.events),
1895
- pointer,
1896
- mouse: pointer,
1897
2000
  ...events
1898
2001
  },
2002
+ size: { ...state.size,
2003
+ ...size
2004
+ },
2005
+ viewport: { ...state.viewport,
2006
+ ...viewport
2007
+ },
1899
2008
  ...rest
1900
2009
  };
1901
2010
  }, [state]);
1902
- const [useInjectStore] = React__namespace.useState(() => {
1903
- const store = create__default['default']((set, get) => ({ ...inject(previousRoot.getState()),
2011
+ const [usePortalStore] = React__namespace.useState(() => {
2012
+ // Create a mirrored store, based on the previous root with a few overrides ...
2013
+ new THREE__namespace.Vector3();
2014
+ const previousState = previousRoot.getState();
2015
+ const store = create__default['default']((set, get) => ({ ...inject(previousState),
2016
+ // Set and get refer to this root-state
1904
2017
  set,
1905
2018
  get,
2019
+ // Layers are allowed to override events
1906
2020
  setEvents: events => set(state => ({ ...state,
1907
2021
  events: { ...state.events,
1908
2022
  ...events
1909
2023
  }
1910
2024
  }))
1911
2025
  }));
1912
- previousRoot.subscribe(state => useInjectStore.setState(injectState => inject(state, injectState)));
1913
2026
  return store;
1914
2027
  });
1915
2028
  React__namespace.useEffect(() => {
1916
- useInjectStore.setState(injectState => inject(previousRoot.getState(), injectState));
2029
+ // Subscribe to previous root-state and copy changes over to the mirrored portal-state
2030
+ const unsub = previousRoot.subscribe(prev => usePortalStore.setState(state => inject(prev, state)));
2031
+ return () => {
2032
+ unsub();
2033
+ usePortalStore.destroy();
2034
+ };
2035
+ }, []);
2036
+ React__namespace.useEffect(() => {
2037
+ usePortalStore.setState(injectState => inject(previousRoot.getState(), injectState));
1917
2038
  }, [inject]);
1918
2039
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, reconciler.createPortal( /*#__PURE__*/React__namespace.createElement(context.Provider, {
1919
- value: useInjectStore
1920
- }, children), useInjectStore, null));
2040
+ value: usePortalStore
2041
+ }, children), usePortalStore, null));
1921
2042
  }
1922
2043
 
1923
2044
  reconciler.injectIntoDevTools({
@@ -1927,6 +2048,8 @@ reconciler.injectIntoDevTools({
1927
2048
  });
1928
2049
  const act = React__namespace.unstable_act;
1929
2050
 
2051
+ exports.Block = Block;
2052
+ exports.ErrorBoundary = ErrorBoundary;
1930
2053
  exports.act = act;
1931
2054
  exports.addAfterEffect = addAfterEffect;
1932
2055
  exports.addEffect = addEffect;
@@ -1950,7 +2073,8 @@ exports.threeTypes = threeTypes;
1950
2073
  exports.unmountComponentAtNode = unmountComponentAtNode;
1951
2074
  exports.useFrame = useFrame;
1952
2075
  exports.useGraph = useGraph;
2076
+ exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
1953
2077
  exports.useLoader = useLoader;
1954
- exports.useMemoizedFn = useMemoizedFn;
2078
+ exports.useMutableCallback = useMutableCallback;
1955
2079
  exports.useStore = useStore;
1956
2080
  exports.useThree = useThree;