@react-three/fiber 8.0.0-beta.5 → 8.0.0-beta.6

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.
@@ -5,17 +5,16 @@ export interface Intersection extends THREE.Intersection {
5
5
  eventObject: THREE.Object3D;
6
6
  }
7
7
  export interface IntersectionEvent<TSourceEvent> extends Intersection {
8
+ eventObject: THREE.Object3D;
8
9
  intersections: Intersection[];
9
- stopped: boolean;
10
10
  unprojectedPoint: THREE.Vector3;
11
+ pointer: THREE.Vector2;
12
+ delta: number;
11
13
  ray: THREE.Ray;
12
14
  camera: Camera;
13
15
  stopPropagation: () => void;
14
- sourceEvent: TSourceEvent;
15
16
  nativeEvent: TSourceEvent;
16
- delta: number;
17
- spaceX: number;
18
- spaceY: number;
17
+ stopped: boolean;
19
18
  }
20
19
  export declare type Camera = THREE.OrthographicCamera | THREE.PerspectiveCamera;
21
20
  export declare type ThreeEvent<TEvent> = IntersectionEvent<TEvent>;
@@ -47,8 +46,14 @@ export declare type EventHandlers = {
47
46
  onPointerCancel?: (event: ThreeEvent<PointerEvent>) => void;
48
47
  onWheel?: (event: ThreeEvent<WheelEvent>) => void;
49
48
  };
49
+ export declare type FilterFunction = (items: THREE.Intersection[], state: RootState) => THREE.Intersection[];
50
+ export declare type ComputeFunction = (event: DomEvent, root: RootState, previous?: RootState) => void;
50
51
  export interface EventManager<TTarget> {
51
- connected: TTarget | boolean;
52
+ enabled: boolean;
53
+ priority: number;
54
+ compute?: ComputeFunction;
55
+ filter?: FilterFunction;
56
+ connected?: TTarget;
52
57
  handlers?: Events;
53
58
  connect?: (target: TTarget) => void;
54
59
  disconnect?: () => void;
@@ -1,6 +1,5 @@
1
1
  import * as THREE from 'three';
2
- import * as React from 'react';
3
- import { StateSelector, EqualityChecker, UseBoundStore } from 'zustand';
2
+ import { StateSelector, EqualityChecker } from 'zustand';
4
3
  import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
5
4
  import { RootState, RenderCallback } from './store';
6
5
  import { ObjectMap } from './utils';
@@ -11,15 +10,16 @@ export declare type Extensions = (loader: THREE.Loader) => void;
11
10
  export declare type LoaderResult<T> = T extends any[] ? Loader<T[number]> : Loader<T>;
12
11
  export declare type ConditionalType<Child, Parent, Truthy, Falsy> = Child extends Parent ? Truthy : Falsy;
13
12
  export declare type BranchingReturn<T, Parent, Coerced> = ConditionalType<T, Parent, Coerced, T>;
14
- export declare function useStore(): UseBoundStore<RootState, import("zustand").StoreApi<RootState>>;
13
+ declare type noop = (...args: any[]) => any;
14
+ declare type PickFunction<T extends noop> = (...args: Parameters<T>) => ReturnType<T>;
15
+ export declare function useStore(): import("zustand").UseBoundStore<RootState, import("zustand").StoreApi<RootState>>;
15
16
  export declare function useThree<T = RootState>(selector?: StateSelector<RootState, T>, equalityFn?: EqualityChecker<T>): T;
16
- export declare function useInject(state: Partial<RootState>): [React.FC<{
17
- children: React.ReactNode;
18
- }>, UseBoundStore<RootState, import("zustand").StoreApi<RootState>>];
19
17
  export declare function useFrame(callback: RenderCallback, renderPriority?: number): null;
20
18
  export declare function useGraph(object: THREE.Object3D): ObjectMap;
19
+ export declare function useMemoizedFn<T extends noop>(fn?: T): PickFunction<T>;
21
20
  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>;
22
21
  export declare namespace useLoader {
23
22
  var preload: <T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U, extensions?: Extensions | undefined) => undefined;
24
23
  var clear: <T, U extends string | string[]>(Proto: new () => LoaderResult<T>, input: U) => void;
25
24
  }
25
+ export {};
@@ -5,8 +5,8 @@ import { UseBoundStore } from 'zustand';
5
5
  import { Renderer, StoreProps, context, RootState, Size } from './store';
6
6
  import { extend, Root } from './renderer';
7
7
  import { addEffect, addAfterEffect, addTail } from './loop';
8
- import { EventManager } from './events';
9
- import { dispose } from './utils';
8
+ import { EventManager, ComputeFunction } from './events';
9
+ import { dispose, getRootState } from './utils';
10
10
  declare const roots: Map<Element, Root>;
11
11
  declare const invalidate: (state?: RootState | undefined) => void, advance: (timestamp: number, runGlobalEffects?: boolean, state?: RootState | undefined, frame?: THREE.XRFrame | undefined) => void;
12
12
  declare const reconciler: import("react-reconciler").Reconciler<unknown, unknown, unknown, unknown, unknown>, applyProps: typeof import("./utils").applyProps;
@@ -28,7 +28,15 @@ export declare type ReconcilerRoot<TCanvas extends Element> = {
28
28
  declare function createRoot<TCanvas extends Element>(canvas: TCanvas): ReconcilerRoot<TCanvas>;
29
29
  declare function render<TCanvas extends Element>(element: React.ReactNode, canvas: TCanvas, config?: RenderProps<TCanvas>): UseBoundStore<RootState>;
30
30
  declare function unmountComponentAtNode<TElement extends Element>(canvas: TElement, callback?: (canvas: TElement) => void): void;
31
- declare function createPortal(children: React.ReactNode, container: THREE.Object3D, state?: Partial<RootState>): React.ReactNode;
31
+ export declare type InjectState = Partial<Omit<RootState, 'set' | 'get' | 'setSize' | 'setFrameloop' | 'setDpr' | 'events' | 'invalidate' | 'advance' | 'performance' | 'internal'> & {
32
+ events: {
33
+ enabled?: boolean;
34
+ priority?: number;
35
+ compute?: ComputeFunction;
36
+ connected?: any;
37
+ };
38
+ }>;
39
+ declare function createPortal(children: React.ReactNode, container: THREE.Object3D, state?: InjectState): React.ReactNode;
32
40
  declare const act: any;
33
41
  export * from './hooks';
34
- export { context, render, createRoot, unmountComponentAtNode, createPortal, reconciler, applyProps, dispose, invalidate, advance, extend, addEffect, addAfterEffect, addTail, act, roots as _roots, };
42
+ export { context, render, createRoot, unmountComponentAtNode, createPortal, reconciler, applyProps, dispose, invalidate, advance, extend, addEffect, addAfterEffect, addTail, getRootState, act, roots as _roots, };
@@ -1,7 +1,7 @@
1
1
  import * as THREE from 'three';
2
2
  import * as React from 'react';
3
3
  import * as ReactThreeFiber from '../three-types';
4
- import { GetState, SetState, UseBoundStore } from 'zustand';
4
+ import { GetState, SetState, StoreApi, UseBoundStore } from 'zustand';
5
5
  import { DomEvent, EventManager, PointerCaptureTarget, ThreeEvent } from './events';
6
6
  export interface Intersection extends THREE.Intersection {
7
7
  eventObject: THREE.Object3D;
@@ -23,11 +23,6 @@ export declare type Viewport = Size & {
23
23
  aspect: number;
24
24
  };
25
25
  export declare type Camera = THREE.OrthographicCamera | THREE.PerspectiveCamera;
26
- export declare type Raycaster = THREE.Raycaster & {
27
- enabled: boolean;
28
- filter?: FilterFunction;
29
- computeOffsets?: ComputeOffsetsFunction;
30
- };
31
26
  export declare type RenderCallback = (state: RootState, delta: number, frame?: THREE.XRFrame) => void;
32
27
  export declare type Performance = {
33
28
  current: number;
@@ -59,7 +54,7 @@ export declare type RootState = {
59
54
  camera: Camera & {
60
55
  manual?: boolean;
61
56
  };
62
- raycaster: Raycaster;
57
+ raycaster: THREE.Raycaster;
63
58
  events: EventManager<any>;
64
59
  xr: {
65
60
  connect: () => void;
@@ -67,7 +62,7 @@ export declare type RootState = {
67
62
  };
68
63
  scene: THREE.Scene;
69
64
  controls: THREE.EventDispatcher | null;
70
- mouse: THREE.Vector2;
65
+ pointer: THREE.Vector2;
71
66
  clock: THREE.Clock;
72
67
  linear: boolean;
73
68
  flat: boolean;
@@ -75,7 +70,7 @@ export declare type RootState = {
75
70
  performance: Performance;
76
71
  size: Size;
77
72
  viewport: Viewport & {
78
- getCurrentViewport: (camera?: Camera, target?: THREE.Vector3, size?: Size) => Omit<Viewport, 'dpr' | 'initialDpr'>;
73
+ getCurrentViewport: (camera?: Camera, target?: THREE.Vector3 | Parameters<THREE.Vector3['set']>, size?: Size) => Omit<Viewport, 'dpr' | 'initialDpr'>;
79
74
  };
80
75
  set: SetState<RootState>;
81
76
  get: GetState<RootState>;
@@ -85,15 +80,9 @@ export declare type RootState = {
85
80
  setDpr: (dpr: Dpr) => void;
86
81
  setFrameloop: (frameloop?: 'always' | 'demand' | 'never') => void;
87
82
  onPointerMissed?: (event: MouseEvent) => void;
83
+ previousRoot?: UseBoundStore<RootState, StoreApi<RootState>>;
88
84
  internal: InternalState;
89
85
  };
90
- export declare type FilterFunction = (items: THREE.Intersection[], state: RootState) => THREE.Intersection[];
91
- export declare type ComputeOffsetsFunction = (event: any, state: RootState) => {
92
- offsetX: number;
93
- offsetY: number;
94
- width?: number;
95
- height?: number;
96
- };
97
86
  export declare type StoreProps = {
98
87
  gl: THREE.WebGLRenderer;
99
88
  size: Size;
@@ -104,12 +93,12 @@ export declare type StoreProps = {
104
93
  frameloop?: 'always' | 'demand' | 'never';
105
94
  performance?: Partial<Omit<Performance, 'regress'>>;
106
95
  dpr?: Dpr;
107
- raycaster?: Partial<Raycaster>;
96
+ raycaster?: Partial<THREE.Raycaster>;
108
97
  camera?: (Camera | Partial<ReactThreeFiber.Object3DNode<THREE.Camera, typeof THREE.Camera> & ReactThreeFiber.Object3DNode<THREE.PerspectiveCamera, typeof THREE.PerspectiveCamera> & ReactThreeFiber.Object3DNode<THREE.OrthographicCamera, typeof THREE.OrthographicCamera>>) & {
109
98
  manual?: boolean;
110
99
  };
111
100
  onPointerMissed?: (event: MouseEvent) => void;
112
101
  };
113
- declare const context: React.Context<UseBoundStore<RootState, import("zustand").StoreApi<RootState>>>;
102
+ declare const context: React.Context<UseBoundStore<RootState, StoreApi<RootState>>>;
114
103
  declare const createStore: (invalidate: (state?: RootState | undefined) => void, advance: (timestamp: number, runGlobalEffects?: boolean | undefined, state?: RootState | undefined, frame?: THREE.XRFrame | undefined) => void) => UseBoundStore<RootState>;
115
104
  export { createStore, context };
@@ -1,6 +1,6 @@
1
1
  import * as THREE from 'three';
2
2
  import { AttachType, Instance, InstanceProps, LocalState } from './renderer';
3
- import { Dpr } from './store';
3
+ import { Dpr, RootState } from './store';
4
4
  export declare const DEFAULT = "__default";
5
5
  export declare type DiffSet = {
6
6
  memoized: {
@@ -21,6 +21,7 @@ export declare type ObjectMap = {
21
21
  };
22
22
  };
23
23
  export declare function calculateDpr(dpr: Dpr): number;
24
+ export declare const getRootState: (obj: THREE.Object3D) => RootState | undefined;
24
25
  export declare function filterKeys<TObj extends {
25
26
  [key: string]: any;
26
27
  }, TOmit extends boolean, TKey extends keyof TObj>(obj: TObj, omit: TOmit, ...keys: TKey[]): TOmit extends true ? Omit<TObj, TKey> : Pick<TObj, TKey>;
@@ -44,6 +44,15 @@ const isDiffSet = def => def && !!def.memoized && !!def.changes;
44
44
  function calculateDpr(dpr) {
45
45
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
46
46
  }
47
+ /**
48
+ * Returns instance root state
49
+ */
50
+
51
+ const getRootState = obj => {
52
+ var _r3f;
53
+
54
+ return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
55
+ };
47
56
  /**
48
57
  * Picks or omits keys from an object
49
58
  * `omit` will filter out keys, and otherwise cherry-pick them.
@@ -53,11 +62,7 @@ function filterKeys(obj, omit, ...keys) {
53
62
  const keysToSelect = new Set(keys);
54
63
  return Object.entries(obj).reduce((acc, [key, value]) => {
55
64
  const shouldInclude = !omit;
56
-
57
- if (keysToSelect.has(key) === shouldInclude) {
58
- acc[key] = value;
59
- }
60
-
65
+ if (keysToSelect.has(key) === shouldInclude) acc[key] = value;
61
66
  return acc;
62
67
  }, {});
63
68
  }
@@ -411,6 +416,7 @@ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
411
416
 
412
417
  function removeInteractivity(store, object) {
413
418
  const {
419
+ events,
414
420
  internal
415
421
  } = store.getState(); // Removes every trace of an object from the data store
416
422
 
@@ -418,6 +424,7 @@ function removeInteractivity(store, object) {
418
424
  internal.initialHits = internal.initialHits.filter(o => o !== object);
419
425
  internal.hovered.forEach((value, key) => {
420
426
  if (value.eventObject === object || value.object === object) {
427
+ // Clear out intersects, they are outdated by now
421
428
  internal.hovered.delete(key);
422
429
  }
423
430
  });
@@ -427,31 +434,8 @@ function removeInteractivity(store, object) {
427
434
  }
428
435
  function createEvents(store) {
429
436
  const temp = new THREE__namespace.Vector3();
430
- /** Sets up defaultRaycaster */
431
-
432
- function prepareRay(event) {
433
- var _customOffsets$offset, _customOffsets$offset2, _customOffsets$width, _customOffsets$height;
434
-
435
- const state = store.getState();
436
- const {
437
- raycaster,
438
- mouse,
439
- camera,
440
- size
441
- } = state; // https://github.com/pmndrs/react-three-fiber/pull/782
442
- // Events trigger outside of canvas when moved
443
-
444
- const customOffsets = raycaster.computeOffsets == null ? void 0 : raycaster.computeOffsets(event, state);
445
- const offsetX = (_customOffsets$offset = customOffsets == null ? void 0 : customOffsets.offsetX) != null ? _customOffsets$offset : event.offsetX;
446
- const offsetY = (_customOffsets$offset2 = customOffsets == null ? void 0 : customOffsets.offsetY) != null ? _customOffsets$offset2 : event.offsetY;
447
- const width = (_customOffsets$width = customOffsets == null ? void 0 : customOffsets.width) != null ? _customOffsets$width : size.width;
448
- const height = (_customOffsets$height = customOffsets == null ? void 0 : customOffsets.height) != null ? _customOffsets$height : size.height;
449
- mouse.set(offsetX / width * 2 - 1, -(offsetY / height) * 2 + 1);
450
- raycaster.setFromCamera(mouse, camera);
451
- }
452
437
  /** Calculates delta */
453
438
 
454
-
455
439
  function calculateDistance(event) {
456
440
  const {
457
441
  internal
@@ -471,55 +455,70 @@ function createEvents(store) {
471
455
  }));
472
456
  }
473
457
 
474
- function intersect(filter) {
458
+ function intersect(event, filter) {
475
459
  const state = store.getState();
476
- const {
477
- raycaster,
478
- internal
479
- } = state; // Skip event handling when noEvents is set
480
-
481
- if (!raycaster.enabled) return [];
482
- const seen = new Set();
460
+ const duplicates = new Set();
483
461
  const intersections = []; // Allow callers to eliminate event objects
484
462
 
485
- const eventsObjects = filter ? filter(internal.interaction) : internal.interaction; // Intersect known handler objects and filter against duplicates
463
+ const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction; // Reset all raycaster cameras to undefined
464
+
465
+ eventsObjects.forEach(obj => {
466
+ const state = getRootState(obj);
467
+
468
+ if (state) {
469
+ state.raycaster.camera = undefined;
470
+ }
471
+ }); // Collect events
472
+
473
+ let hits = eventsObjects // Intersect objects
474
+ .flatMap(obj => {
475
+ const state = getRootState(obj); // Skip event handling when noEvents is set, or when the raycasters camera is null
476
+
477
+ if (!state || !state.events.enabled || state.raycaster.camera === null) return []; // When the camera is undefined we have to call the event layers update function
478
+
479
+ if (state.raycaster.camera === undefined) {
480
+ var _state$previousRoot;
481
+
482
+ state.events.compute == null ? void 0 : state.events.compute(event, state, (_state$previousRoot = state.previousRoot) == null ? void 0 : _state$previousRoot.getState()); // If the camera is still undefined we have to skip this layer entirely
486
483
 
487
- let intersects = raycaster.intersectObjects(eventsObjects, true).filter(item => {
484
+ if (state.raycaster.camera === undefined) state.raycaster.camera = null;
485
+ } // Intersect object by object
486
+
487
+
488
+ return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : [];
489
+ }) // Sort by event priority and distance
490
+ .sort((a, b) => {
491
+ const aState = getRootState(a.object);
492
+ const bState = getRootState(b.object);
493
+ if (!aState || !bState) return 0;
494
+ return bState.events.priority - aState.events.priority || a.distance - b.distance;
495
+ }) // Filter out duplicates
496
+ .filter(item => {
488
497
  const id = makeId(item);
489
- if (seen.has(id)) return false;
490
- seen.add(id);
498
+ if (duplicates.has(id)) return false;
499
+ duplicates.add(id);
491
500
  return true;
492
501
  }); // https://github.com/mrdoob/three.js/issues/16031
493
- // Allow custom userland intersect sort order
502
+ // Allow custom userland intersect sort order, this likely only makes sense on the root filter
494
503
 
495
- if (raycaster.filter) intersects = raycaster.filter(intersects, state);
504
+ if (state.events.filter) hits = state.events.filter(hits, state); // Bubble up the events, find the event source (eventObject)
496
505
 
497
- for (const intersect of intersects) {
498
- let eventObject = intersect.object; // Bubble event up
506
+ for (const hit of hits) {
507
+ let eventObject = hit.object; // Bubble event up
499
508
 
500
509
  while (eventObject) {
501
510
  var _r3f2;
502
511
 
503
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
512
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...hit,
504
513
  eventObject
505
514
  });
506
515
  eventObject = eventObject.parent;
507
516
  }
508
- }
517
+ } // If the interaction is captured, make all capturing targets part of the intersect.
509
518
 
510
- return intersections;
511
- }
512
- /** Creates filtered intersects and returns an array of positive hits */
513
-
514
-
515
- function patchIntersects(intersections, event) {
516
- const {
517
- internal
518
- } = store.getState(); // If the interaction is captured, make all capturing targets part of the
519
- // intersect.
520
519
 
521
- if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
522
- for (let captureData of internal.capturedMap.get(event.pointerId).values()) {
520
+ if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
521
+ for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
523
522
  intersections.push(captureData.intersection);
524
523
  }
525
524
  }
@@ -532,13 +531,13 @@ function createEvents(store) {
532
531
  function handleIntersects(intersections, event, delta, callback) {
533
532
  const {
534
533
  raycaster,
535
- mouse,
534
+ pointer,
536
535
  camera,
537
536
  internal
538
537
  } = store.getState(); // If anything has been found, forward it to the event listeners
539
538
 
540
539
  if (intersections.length) {
541
- const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
540
+ const unprojectedPoint = temp.set(pointer.x, pointer.y, 0).unproject(camera);
542
541
  const localState = {
543
542
  stopped: false
544
543
  };
@@ -589,8 +588,7 @@ function createEvents(store) {
589
588
 
590
589
  let raycastEvent = { ...hit,
591
590
  ...extractEventProps,
592
- spaceX: mouse.x,
593
- spaceY: mouse.y,
591
+ pointer,
594
592
  intersections,
595
593
  stopped: localState.stopped,
596
594
  delta,
@@ -627,8 +625,6 @@ function createEvents(store) {
627
625
  setPointerCapture,
628
626
  releasePointerCapture
629
627
  },
630
- sourceEvent: event,
631
- // deprecated
632
628
  nativeEvent: event
633
629
  }; // Call subscribers
634
630
 
@@ -693,14 +689,15 @@ function createEvents(store) {
693
689
  const {
694
690
  onPointerMissed,
695
691
  internal
696
- } = store.getState();
697
- prepareRay(event);
692
+ } = store.getState(); //prepareRay(event)
693
+
698
694
  internal.lastEvent.current = event; // Get fresh intersects
699
695
 
700
696
  const isPointerMove = name === 'onPointerMove';
701
697
  const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
702
- const filter = isPointerMove ? filterPointerEvents : undefined;
703
- const hits = patchIntersects(intersect(filter), event);
698
+ const filter = isPointerMove ? filterPointerEvents : undefined; //const hits = patchIntersects(intersect(filter), event)
699
+
700
+ const hits = intersect(event, filter);
704
701
  const delta = isClickEvent ? calculateDistance(event) : 0; // Save initial coordinates on pointer-down
705
702
 
706
703
  if (name === 'onPointerDown') {
@@ -1180,16 +1177,18 @@ const createStore = (invalidate, advance) => {
1180
1177
  }));
1181
1178
 
1182
1179
  return {
1180
+ set,
1181
+ get,
1183
1182
  // Mock objects that have to be configured
1184
1183
  gl: null,
1185
1184
  camera: null,
1186
1185
  raycaster: null,
1187
1186
  events: {
1187
+ priority: 1,
1188
+ enabled: true,
1188
1189
  connected: false
1189
1190
  },
1190
1191
  xr: null,
1191
- set,
1192
- get,
1193
1192
  invalidate: () => invalidate(get()),
1194
1193
  advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1195
1194
  linear: false,
@@ -1197,7 +1196,7 @@ const createStore = (invalidate, advance) => {
1197
1196
  scene: prepare(new THREE__namespace.Scene()),
1198
1197
  controls: null,
1199
1198
  clock: new THREE__namespace.Clock(),
1200
- mouse: new THREE__namespace.Vector2(),
1199
+ pointer: new THREE__namespace.Vector2(),
1201
1200
  frameloop: 'always',
1202
1201
  onPointerMissed: undefined,
1203
1202
  performance: {
@@ -1266,6 +1265,7 @@ const createStore = (invalidate, advance) => {
1266
1265
  frameloop
1267
1266
  }));
1268
1267
  },
1268
+ previousRoot: undefined,
1269
1269
  internal: {
1270
1270
  active: false,
1271
1271
  priority: 0,
@@ -1467,43 +1467,6 @@ function useStore() {
1467
1467
  function useThree(selector = state => state, equalityFn) {
1468
1468
  return useStore()(selector, equalityFn);
1469
1469
  }
1470
- function useInject(state) {
1471
- const useOriginalStore = useStore();
1472
- const useInjectStore = React__namespace.useMemo(() => {
1473
- const useInjected = (sel = state => state) => {
1474
- // Execute the useStore hook with the selector once, to maintain reactivity, result doesn't matter
1475
- useOriginalStore(sel); // Inject data and return the result, either selected or raw
1476
-
1477
- return sel({ ...useOriginalStore.getState(),
1478
- ...state
1479
- });
1480
- };
1481
-
1482
- useInjected.setState = useOriginalStore.setState;
1483
- useInjected.destroy = useOriginalStore.destroy; // Patch getState
1484
-
1485
- useInjected.getState = () => {
1486
- return { ...useOriginalStore.getState(),
1487
- ...state
1488
- };
1489
- }; // Patch subscribe
1490
-
1491
-
1492
- useInjected.subscribe = listener => {
1493
- return useOriginalStore.subscribe((current, previous) => listener({ ...current,
1494
- ...state
1495
- }, previous));
1496
- };
1497
-
1498
- return useInjected;
1499
- }, [useOriginalStore, state]); // Return the patched store and a provider component
1500
-
1501
- return React__namespace.useMemo(() => [({
1502
- children
1503
- }) => /*#__PURE__*/React__namespace.createElement(context.Provider, {
1504
- value: useInjectStore
1505
- }, children), useInjectStore], [useInjectStore]);
1506
- }
1507
1470
  function useFrame(callback, renderPriority = 0) {
1508
1471
  const subscribe = useStore().getState().internal.subscribe; // Update ref
1509
1472
 
@@ -1530,6 +1493,11 @@ function loadingFn(extensions, onProgress) {
1530
1493
  };
1531
1494
  }
1532
1495
 
1496
+ function useMemoizedFn(fn) {
1497
+ const fnRef = React__namespace.useRef(fn);
1498
+ React__namespace.useLayoutEffect(() => void (fnRef.current = fn), [fn]);
1499
+ return (...args) => fnRef.current == null ? void 0 : fnRef.current(...args);
1500
+ }
1533
1501
  function useLoader(Proto, input, extensions, onProgress) {
1534
1502
  // Use suspense to load async assets
1535
1503
  const keys = Array.isArray(input) ? input : [input];
@@ -1629,9 +1597,7 @@ function createRoot(canvas) {
1629
1597
  params,
1630
1598
  ...options
1631
1599
  } = raycastOptions || {};
1632
- if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, {
1633
- enabled: true,
1634
- ...options
1600
+ if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options
1635
1601
  });
1636
1602
  if (!is.equ(params, raycaster.params, shallowLoose)) applyProps(raycaster, {
1637
1603
  params: { ...raycaster.params,
@@ -1785,9 +1751,10 @@ function Provider({
1785
1751
  internal: { ...state.internal,
1786
1752
  active: true
1787
1753
  }
1788
- })); // Connect events
1754
+ })); // Connect events to the targets parent, this is done to ensure events are registered on
1755
+ // a shared target, and not on the canvas itself
1789
1756
 
1790
- state.events.connect == null ? void 0 : state.events.connect(target); // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
1757
+ state.events.connect == null ? void 0 : state.events.connect(target.parentNode); // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
1791
1758
 
1792
1759
  if (onCreated) onCreated(state); // eslint-disable-next-line react-hooks/exhaustive-deps
1793
1760
  }, []);
@@ -1834,20 +1801,56 @@ function createPortal(children, container, state) {
1834
1801
  }
1835
1802
 
1836
1803
  function Portal({
1837
- state,
1804
+ state = {},
1838
1805
  children,
1839
1806
  container
1840
1807
  }) {
1841
1808
  /** This has to be a component because it would not be able to call useThree/useStore otherwise since
1842
- * if this is our environment, then we are in in r3f's renderer but in react-dom, it would trigger
1809
+ * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
1843
1810
  * the "R3F hooks can only be used within the Canvas component!" warning:
1844
1811
  * <Canvas>
1845
1812
  * {createPortal(...)} */
1846
- const portalState = React__namespace.useMemo(() => ({ ...state,
1847
- scene: container
1848
- }), [state, container]);
1849
- const [PortalProvider, portalRoot] = useInject(portalState);
1850
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, reconciler.createPortal( /*#__PURE__*/React__namespace.createElement(PortalProvider, null, children), portalRoot, null));
1813
+ const {
1814
+ events,
1815
+ ...rest
1816
+ } = state;
1817
+ const previousRoot = useStore();
1818
+ const [raycaster] = React__namespace.useState(() => new THREE__namespace.Raycaster());
1819
+ const inject = React__namespace.useCallback((state, injectState) => {
1820
+ const intersect = { ...state
1821
+ };
1822
+
1823
+ if (injectState) {
1824
+ // Only the fields of "state" that do not differ from injectState
1825
+ Object.keys(state).forEach(key => {
1826
+ if (state[key] !== injectState[key]) delete intersect[key];
1827
+ });
1828
+ }
1829
+
1830
+ return { ...intersect,
1831
+ scene: container,
1832
+ previousRoot,
1833
+ raycaster,
1834
+ events: { ...state.events,
1835
+ ...events
1836
+ },
1837
+ ...rest
1838
+ };
1839
+ }, [state]);
1840
+ const [useInjectStore] = React__namespace.useState(() => {
1841
+ const store = create__default['default']((set, get) => ({ ...inject(previousRoot.getState()),
1842
+ set,
1843
+ get
1844
+ }));
1845
+ previousRoot.subscribe(state => useInjectStore.setState(injectState => inject(state, injectState)));
1846
+ return store;
1847
+ });
1848
+ React__namespace.useEffect(() => {
1849
+ useInjectStore.setState(injectState => inject(previousRoot.getState(), injectState));
1850
+ }, [inject]);
1851
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, reconciler.createPortal( /*#__PURE__*/React__namespace.createElement(context.Provider, {
1852
+ value: useInjectStore
1853
+ }, children), useInjectStore, null));
1851
1854
  }
1852
1855
 
1853
1856
  reconciler.injectIntoDevTools({
@@ -1869,6 +1872,7 @@ exports.createPortal = createPortal;
1869
1872
  exports.createRoot = createRoot;
1870
1873
  exports.dispose = dispose;
1871
1874
  exports.extend = extend;
1875
+ exports.getRootState = getRootState;
1872
1876
  exports.invalidate = invalidate;
1873
1877
  exports.omit = omit;
1874
1878
  exports.pick = pick;
@@ -1879,7 +1883,7 @@ exports.threeTypes = threeTypes;
1879
1883
  exports.unmountComponentAtNode = unmountComponentAtNode;
1880
1884
  exports.useFrame = useFrame;
1881
1885
  exports.useGraph = useGraph;
1882
- exports.useInject = useInject;
1883
1886
  exports.useLoader = useLoader;
1887
+ exports.useMemoizedFn = useMemoizedFn;
1884
1888
  exports.useStore = useStore;
1885
1889
  exports.useThree = useThree;