@react-three/fiber 8.0.0-beta.4 → 8.0.0-beta.7

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.
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
4
- var suspendReact = require('suspend-react');
5
3
  var THREE = require('three');
4
+ var React = require('react');
6
5
  var constants = require('react-reconciler/constants');
7
6
  var create = require('zustand');
8
7
  var Reconciler = require('react-reconciler');
9
8
  var scheduler = require('scheduler');
9
+ var suspendReact = require('suspend-react');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
12
12
 
@@ -30,8 +30,8 @@ function _interopNamespace(e) {
30
30
  return Object.freeze(n);
31
31
  }
32
32
 
33
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
34
33
  var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
34
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
35
35
  var create__default = /*#__PURE__*/_interopDefault(create);
36
36
  var Reconciler__default = /*#__PURE__*/_interopDefault(Reconciler);
37
37
 
@@ -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
  }
@@ -95,7 +100,7 @@ const is = {
95
100
  const isArr = is.arr(a);
96
101
  if (isArr && arrays === 'reference') return a === b; // Array or Object, shallow compare first to see if it's a match
97
102
 
98
- if ((isArr || isObj) && a == b) return true; // Last resort, go through keys
103
+ if ((isArr || isObj) && a === b) return true; // Last resort, go through keys
99
104
 
100
105
  let i;
101
106
 
@@ -103,7 +108,13 @@ const is = {
103
108
 
104
109
  for (i in strict ? b : a) if (a[i] !== b[i]) return false;
105
110
 
106
- return is.und(i) ? a === b : true;
111
+ if (is.und(i)) {
112
+ if (isArr && a.length === 0 && b.length === 0) return true;
113
+ if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
114
+ if (a !== b) return false;
115
+ }
116
+
117
+ return true;
107
118
  }
108
119
 
109
120
  }; // Collects nodes and materials from a THREE.Object3D
@@ -139,30 +150,8 @@ function prepare(object, state) {
139
150
  if (state != null && state.primitive || !instance.__r3f) {
140
151
  instance.__r3f = {
141
152
  type: '',
142
- context: {
143
- current: null
144
- },
145
- getContext: () => {
146
- const injects = [];
147
- let inject = instance.__r3f.context.current;
148
-
149
- while (inject) {
150
- var _inject$memoizedProps, _inject, _inject$context;
151
-
152
- const {
153
- children,
154
- args,
155
- ...props
156
- } = (_inject$memoizedProps = (_inject = inject) == null ? void 0 : _inject.memoizedProps) != null ? _inject$memoizedProps : {};
157
- injects.push(props);
158
- inject = (_inject$context = inject.context) == null ? void 0 : _inject$context.current;
159
- }
160
-
161
- return injects.reverse().reduce((prev, cur) => ({ ...prev,
162
- ...cur
163
- }), {});
164
- },
165
153
  root: null,
154
+ previousAttach: null,
166
155
  memoizedProps: {},
167
156
  eventCount: 0,
168
157
  handlers: {},
@@ -190,32 +179,42 @@ function resolve(instance, key) {
190
179
  target,
191
180
  key
192
181
  };
193
- }
182
+ } // Checks if a dash-cased string ends with an integer
194
183
 
184
+
185
+ const INDEX_REGEX = /-\d+$/;
195
186
  function attach(parent, child, type) {
196
187
  if (is.str(type)) {
188
+ // If attaching into an array (foo-0), create one
189
+ if (INDEX_REGEX.test(type)) {
190
+ const root = type.replace(INDEX_REGEX, '');
191
+ const {
192
+ target,
193
+ key
194
+ } = resolve(parent, root);
195
+ if (!Array.isArray(target[key])) target[key] = [];
196
+ }
197
+
197
198
  const {
198
199
  target,
199
200
  key
200
201
  } = resolve(parent, type);
201
- parent.__r3f.previousAttach = target[key];
202
+ child.__r3f.previousAttach = target[key];
202
203
  target[key] = child;
203
- } else if (is.arr(type)) {
204
- const [attach] = type;
205
- if (is.str(attach)) parent[attach](child);else if (is.fun(attach)) attach(parent, child);
206
- }
204
+ } else child.__r3f.previousAttach = type(parent, child);
207
205
  }
208
206
  function detach(parent, child, type) {
207
+ var _child$__r3f, _child$__r3f2;
208
+
209
209
  if (is.str(type)) {
210
210
  const {
211
211
  target,
212
212
  key
213
213
  } = resolve(parent, type);
214
- target[key] = parent.__r3f.previousAttach;
215
- } else if (is.arr(type)) {
216
- const [, detach] = type;
217
- if (is.str(detach)) parent[detach](child);else if (is.fun(detach)) detach(parent, child);
218
- }
214
+ target[key] = child.__r3f.previousAttach;
215
+ } else (_child$__r3f = child.__r3f) == null ? void 0 : _child$__r3f.previousAttach == null ? void 0 : _child$__r3f.previousAttach(parent, child);
216
+
217
+ (_child$__r3f2 = child.__r3f) == null ? true : delete _child$__r3f2.previousAttach;
219
218
  } // This function prepares a set of changes to be applied to the instance
220
219
 
221
220
  function diffProps(instance, {
@@ -335,20 +334,10 @@ function applyProps$1(instance, data) {
335
334
 
336
335
  if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
337
336
  else if (targetProp instanceof THREE__namespace.Layers && value instanceof THREE__namespace.Layers) targetProp.mask = value.mask; // Otherwise just set ...
338
- else targetProp.set(value); // Auto-convert sRGB colors, for now ...
339
- // https://github.com/pmndrs/react-three-fiber/issues/344
340
-
341
- if (!rootState.linear && isColor) targetProp.convertSRGBToLinear();
337
+ else targetProp.set(value);
342
338
  } // Else, just overwrite the value
343
339
 
344
- } else {
345
- currentInstance[key] = value; // Auto-convert sRGB textures, for now ...
346
- // https://github.com/pmndrs/react-three-fiber/issues/344
347
-
348
- if (!rootState.linear && currentInstance[key] instanceof THREE__namespace.Texture) {
349
- currentInstance[key].encoding = THREE__namespace.sRGBEncoding;
350
- }
351
- }
340
+ } else currentInstance[key] = value;
352
341
 
353
342
  invalidateInstance(instance);
354
343
  return instance;
@@ -427,6 +416,7 @@ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
427
416
 
428
417
  function removeInteractivity(store, object) {
429
418
  const {
419
+ events,
430
420
  internal
431
421
  } = store.getState(); // Removes every trace of an object from the data store
432
422
 
@@ -434,6 +424,7 @@ function removeInteractivity(store, object) {
434
424
  internal.initialHits = internal.initialHits.filter(o => o !== object);
435
425
  internal.hovered.forEach((value, key) => {
436
426
  if (value.eventObject === object || value.object === object) {
427
+ // Clear out intersects, they are outdated by now
437
428
  internal.hovered.delete(key);
438
429
  }
439
430
  });
@@ -443,31 +434,8 @@ function removeInteractivity(store, object) {
443
434
  }
444
435
  function createEvents(store) {
445
436
  const temp = new THREE__namespace.Vector3();
446
- /** Sets up defaultRaycaster */
447
-
448
- function prepareRay(event) {
449
- var _customOffsets$offset, _customOffsets$offset2, _customOffsets$width, _customOffsets$height;
450
-
451
- const state = store.getState();
452
- const {
453
- raycaster,
454
- mouse,
455
- camera,
456
- size
457
- } = state; // https://github.com/pmndrs/react-three-fiber/pull/782
458
- // Events trigger outside of canvas when moved
459
-
460
- const customOffsets = raycaster.computeOffsets == null ? void 0 : raycaster.computeOffsets(event, state);
461
- const offsetX = (_customOffsets$offset = customOffsets == null ? void 0 : customOffsets.offsetX) != null ? _customOffsets$offset : event.offsetX;
462
- const offsetY = (_customOffsets$offset2 = customOffsets == null ? void 0 : customOffsets.offsetY) != null ? _customOffsets$offset2 : event.offsetY;
463
- const width = (_customOffsets$width = customOffsets == null ? void 0 : customOffsets.width) != null ? _customOffsets$width : size.width;
464
- const height = (_customOffsets$height = customOffsets == null ? void 0 : customOffsets.height) != null ? _customOffsets$height : size.height;
465
- mouse.set(offsetX / width * 2 - 1, -(offsetY / height) * 2 + 1);
466
- raycaster.setFromCamera(mouse, camera);
467
- }
468
437
  /** Calculates delta */
469
438
 
470
-
471
439
  function calculateDistance(event) {
472
440
  const {
473
441
  internal
@@ -487,55 +455,70 @@ function createEvents(store) {
487
455
  }));
488
456
  }
489
457
 
490
- function intersect(filter) {
458
+ function intersect(event, filter) {
491
459
  const state = store.getState();
492
- const {
493
- raycaster,
494
- internal
495
- } = state; // Skip event handling when noEvents is set
496
-
497
- if (!raycaster.enabled) return [];
498
- const seen = new Set();
460
+ const duplicates = new Set();
499
461
  const intersections = []; // Allow callers to eliminate event objects
500
462
 
501
- 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
502
478
 
503
- let intersects = raycaster.intersectObjects(eventsObjects, true).filter(item => {
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
483
+
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 => {
504
497
  const id = makeId(item);
505
- if (seen.has(id)) return false;
506
- seen.add(id);
498
+ if (duplicates.has(id)) return false;
499
+ duplicates.add(id);
507
500
  return true;
508
501
  }); // https://github.com/mrdoob/three.js/issues/16031
509
- // Allow custom userland intersect sort order
502
+ // Allow custom userland intersect sort order, this likely only makes sense on the root filter
510
503
 
511
- 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)
512
505
 
513
- for (const intersect of intersects) {
514
- let eventObject = intersect.object; // Bubble event up
506
+ for (const hit of hits) {
507
+ let eventObject = hit.object; // Bubble event up
515
508
 
516
509
  while (eventObject) {
517
510
  var _r3f2;
518
511
 
519
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
512
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...hit,
520
513
  eventObject
521
514
  });
522
515
  eventObject = eventObject.parent;
523
516
  }
524
- }
517
+ } // If the interaction is captured, make all capturing targets part of the intersect.
525
518
 
526
- return intersections;
527
- }
528
- /** Creates filtered intersects and returns an array of positive hits */
529
519
 
530
-
531
- function patchIntersects(intersections, event) {
532
- const {
533
- internal
534
- } = store.getState(); // If the interaction is captured, make all capturing targets part of the
535
- // intersect.
536
-
537
- if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
538
- 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()) {
539
522
  intersections.push(captureData.intersection);
540
523
  }
541
524
  }
@@ -548,13 +531,13 @@ function createEvents(store) {
548
531
  function handleIntersects(intersections, event, delta, callback) {
549
532
  const {
550
533
  raycaster,
551
- mouse,
534
+ pointer,
552
535
  camera,
553
536
  internal
554
537
  } = store.getState(); // If anything has been found, forward it to the event listeners
555
538
 
556
539
  if (intersections.length) {
557
- const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
540
+ const unprojectedPoint = temp.set(pointer.x, pointer.y, 0).unproject(camera);
558
541
  const localState = {
559
542
  stopped: false
560
543
  };
@@ -605,8 +588,7 @@ function createEvents(store) {
605
588
 
606
589
  let raycastEvent = { ...hit,
607
590
  ...extractEventProps,
608
- spaceX: mouse.x,
609
- spaceY: mouse.y,
591
+ pointer,
610
592
  intersections,
611
593
  stopped: localState.stopped,
612
594
  delta,
@@ -643,8 +625,6 @@ function createEvents(store) {
643
625
  setPointerCapture,
644
626
  releasePointerCapture
645
627
  },
646
- sourceEvent: event,
647
- // deprecated
648
628
  nativeEvent: event
649
629
  }; // Call subscribers
650
630
 
@@ -709,14 +689,15 @@ function createEvents(store) {
709
689
  const {
710
690
  onPointerMissed,
711
691
  internal
712
- } = store.getState();
713
- prepareRay(event);
692
+ } = store.getState(); //prepareRay(event)
693
+
714
694
  internal.lastEvent.current = event; // Get fresh intersects
715
695
 
716
696
  const isPointerMove = name === 'onPointerMove';
717
697
  const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
718
- const filter = isPointerMove ? filterPointerEvents : undefined;
719
- 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);
720
701
  const delta = isClickEvent ? calculateDistance(event) : 0; // Save initial coordinates on pointer-down
721
702
 
722
703
  if (name === 'onPointerDown') {
@@ -799,54 +780,20 @@ function createEvents(store) {
799
780
  };
800
781
  }
801
782
 
802
- // Type guard to tell a store from a portal
803
- const isStore = def => def && !!def.getState;
804
-
805
- const getContainer = (container, child) => {
806
- var _container$__r3f$root, _container$__r3f;
807
-
808
- return {
809
- // If the container is not a root-store then it must be a THREE.Object3D into which part of the
810
- // scene is portalled into. Now there can be two variants of this, either that object is part of
811
- // the regular jsx tree, in which case it already has __r3f with a valid root attached, or it lies
812
- // outside react, in which case we must take the root of the child that is about to be attached to it.
813
- root: isStore(container) ? container : (_container$__r3f$root = (_container$__r3f = container.__r3f) == null ? void 0 : _container$__r3f.root) != null ? _container$__r3f$root : child.__r3f.root,
814
- // The container is the eventual target into which objects are mounted, it has to be a THREE.Object3D
815
- container: isStore(container) ? container.getState().scene : container
816
- };
817
- };
818
-
819
783
  let catalogue = {};
820
784
 
821
785
  let extend = objects => void (catalogue = { ...catalogue,
822
786
  ...objects
823
787
  });
824
788
 
825
- extend({
826
- Inject: THREE__namespace.Group
827
- });
828
-
829
789
  function createRenderer(roots, getEventPriority) {
830
790
  function createInstance(type, {
831
791
  args = [],
832
792
  attach,
833
793
  ...props
834
- }, root, context, internalInstanceHandle) {
794
+ }, root) {
835
795
  let name = `${type[0].toUpperCase()}${type.slice(1)}`;
836
- let instance; // https://github.com/facebook/react/issues/17147
837
- // Portals do not give us a root, they are themselves treated as a root by the reconciler
838
- // In order to figure out the actual root we have to climb through fiber internals :(
839
-
840
- if (!isStore(root) && internalInstanceHandle) {
841
- const fn = node => {
842
- if (!node.return) return node.stateNode && node.stateNode.containerInfo;else return fn(node.return);
843
- };
844
-
845
- root = fn(internalInstanceHandle);
846
- } // Assert that by now we have a valid root
847
-
848
-
849
- if (!root || !isStore(root)) throw `No valid root for ${name}!`; // Auto-attach geometries and materials
796
+ let instance; // Auto-attach geometries and materials
850
797
 
851
798
  if (attach === undefined) {
852
799
  if (name.endsWith('Geometry')) attach = 'geometry';else if (name.endsWith('Material')) attach = 'material';
@@ -858,7 +805,6 @@ function createRenderer(roots, getEventPriority) {
858
805
  instance = prepare(object, {
859
806
  type,
860
807
  root,
861
- context,
862
808
  attach,
863
809
  primitive: true
864
810
  });
@@ -876,7 +822,6 @@ function createRenderer(roots, getEventPriority) {
876
822
  instance = prepare(new target(...args), {
877
823
  type,
878
824
  root,
879
- context,
880
825
  attach,
881
826
  // TODO: Figure out what this is for
882
827
  memoizedProps: {
@@ -894,15 +839,6 @@ function createRenderer(roots, getEventPriority) {
894
839
  }
895
840
 
896
841
  function appendChild(parentInstance, child) {
897
- // https://github.com/facebook/react/issues/24138
898
- // Injects are special purpose "onion layers" that inject contextual information into the scene graph.
899
- // Since react-reconciler does not allow us to access the current host context we trick it by leading
900
- // back to it from the first child that is added to it. We just connect the inject to it's own host context.
901
- if ((parentInstance == null ? void 0 : parentInstance.__r3f.type) === 'inject') {
902
- const context = child == null ? void 0 : child.__r3f.context;
903
- if (context) context.current = parentInstance.__r3f;
904
- }
905
-
906
842
  let added = false;
907
843
 
908
844
  if (child) {
@@ -998,7 +934,6 @@ function createRenderer(roots, getEventPriority) {
998
934
 
999
935
  if (child.__r3f) {
1000
936
  delete child.__r3f.root;
1001
- delete child.__r3f.context;
1002
937
  delete child.__r3f.objects;
1003
938
  delete child.__r3f.handlers;
1004
939
  delete child.__r3f.memoizedProps;
@@ -1021,11 +956,11 @@ function createRenderer(roots, getEventPriority) {
1021
956
  }
1022
957
 
1023
958
  function switchInstance(instance, type, newProps, fiber) {
1024
- var _instance$__r3f;
959
+ var _instance$__r3f, _instance$__r3f2;
1025
960
 
1026
961
  const parent = (_instance$__r3f = instance.__r3f) == null ? void 0 : _instance$__r3f.parent;
1027
962
  if (!parent) return;
1028
- const newInstance = createInstance(type, newProps, instance.__r3f.root); // https://github.com/pmndrs/react-three-fiber/issues/1348
963
+ const newInstance = createInstance(type, newProps, (_instance$__r3f2 = instance.__r3f) == null ? void 0 : _instance$__r3f2.root); // https://github.com/pmndrs/react-three-fiber/issues/1348
1029
964
  // When args change the instance has to be re-constructed, which then
1030
965
  // forces r3f to re-parent the children and non-scene objects
1031
966
  // This can not include primitives, which should not have declarative children
@@ -1068,46 +1003,28 @@ function createRenderer(roots, getEventPriority) {
1068
1003
  supportsMutation: true,
1069
1004
  isPrimaryRenderer: false,
1070
1005
  noTimeout: -1,
1071
- appendChildToContainer: (parentInstance, child) => {
1072
- const {
1073
- container,
1074
- root
1075
- } = getContainer(parentInstance, child); // Link current root to the default scene
1006
+ appendChildToContainer: (container, child) => {
1007
+ const scene = container.getState().scene; // Link current root to the default scene
1076
1008
 
1077
- container.__r3f.root = root;
1078
- appendChild(container, child);
1079
- },
1080
- removeChildFromContainer: (parentInstance, child) => removeChild(getContainer(parentInstance, child).container, child),
1081
- insertInContainerBefore: (parentInstance, child, beforeChild) => insertBefore(getContainer(parentInstance, child).container, child, beforeChild),
1082
- getRootHostContext: () => ({
1083
- current: null
1084
- }),
1085
- getChildHostContext: (parentHostContext, type) => {
1086
- // This is a little misleading, this function does not determine the host context for the element at hand,
1087
- // but rather for all the children of it. The context for an inject is and will be the scene, everything
1088
- // within an inject is contextual to it.
1089
- if (type === 'inject') return {
1090
- current: null
1091
- };
1092
- return parentHostContext;
1009
+ scene.__r3f.root = container;
1010
+ appendChild(scene, child);
1093
1011
  },
1012
+ removeChildFromContainer: (container, child) => removeChild(container.getState().scene, child),
1013
+ insertInContainerBefore: (container, child, beforeChild) => insertBefore(container.getState().scene, child, beforeChild),
1014
+ getRootHostContext: () => null,
1015
+ getChildHostContext: parentHostContext => parentHostContext,
1094
1016
 
1095
1017
  finalizeInitialChildren(instance) {
1096
- var _instance$__r3f2;
1018
+ var _instance$__r3f3;
1097
1019
 
1098
- const localState = (_instance$__r3f2 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f2 : {}; // https://github.com/facebook/react/issues/20271
1020
+ const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {}; // https://github.com/facebook/react/issues/20271
1099
1021
  // Returning true will trigger commitMount
1100
1022
 
1101
1023
  return !!localState.handlers;
1102
1024
  },
1103
1025
 
1104
1026
  prepareUpdate(instance, type, oldProps, newProps) {
1105
- // Injects are special purpose "onion layers" that inject contextual information into the scene graph
1106
- // Because the context of an inject is still the scene we have to rely on children to give us the inject-context
1107
- // so that we can set up props.
1108
- if (type === 'inject' && instance.children.length) ; // Create diff-sets
1109
-
1110
-
1027
+ // Create diff-sets
1111
1028
  if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) {
1112
1029
  return [true];
1113
1030
  } else {
@@ -1141,11 +1058,11 @@ function createRenderer(roots, getEventPriority) {
1141
1058
  },
1142
1059
 
1143
1060
  commitMount(instance, type, props, int) {
1144
- var _instance$__r3f3;
1061
+ var _instance$__r3f4;
1145
1062
 
1146
1063
  // https://github.com/facebook/react/issues/20271
1147
1064
  // This will make sure events are only added once to the central container
1148
- const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
1065
+ const localState = (_instance$__r3f4 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f4 : {};
1149
1066
 
1150
1067
  if (instance.raycast && localState.handlers && localState.eventCount) {
1151
1068
  instance.__r3f.root.getState().internal.interaction.push(instance);
@@ -1155,7 +1072,7 @@ function createRenderer(roots, getEventPriority) {
1155
1072
  getPublicInstance: instance => instance,
1156
1073
  shouldDeprioritizeSubtree: () => false,
1157
1074
  prepareForCommit: () => null,
1158
- preparePortalMount: containerInfo => prepare(containerInfo),
1075
+ preparePortalMount: container => prepare(container.getState().scene),
1159
1076
  resetAfterCommit: () => {},
1160
1077
  shouldSetTextContent: () => false,
1161
1078
  clearContainer: () => false,
@@ -1163,26 +1080,26 @@ function createRenderer(roots, getEventPriority) {
1163
1080
  createTextInstance: () => {},
1164
1081
 
1165
1082
  hideInstance(instance) {
1166
- var _instance$__r3f4;
1083
+ var _instance$__r3f5;
1167
1084
 
1168
1085
  // Deatch while the instance is hidden
1169
1086
  const {
1170
1087
  attach: type,
1171
1088
  parent
1172
- } = (_instance$__r3f4 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f4 : {};
1089
+ } = (_instance$__r3f5 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f5 : {};
1173
1090
  if (type && parent) detach(parent, instance, type);
1174
1091
  if (instance.isObject3D) instance.visible = false;
1175
1092
  invalidateInstance(instance);
1176
1093
  },
1177
1094
 
1178
1095
  unhideInstance(instance, props) {
1179
- var _instance$__r3f5;
1096
+ var _instance$__r3f6;
1180
1097
 
1181
1098
  // Re-attach when the instance is unhidden
1182
1099
  const {
1183
1100
  attach: type,
1184
1101
  parent
1185
- } = (_instance$__r3f5 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f5 : {};
1102
+ } = (_instance$__r3f6 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f6 : {};
1186
1103
  if (type && parent) attach(parent, instance, type);
1187
1104
  if (instance.isObject3D && props.visible == null || props.visible) instance.visible = true;
1188
1105
  invalidateInstance(instance);
@@ -1260,16 +1177,18 @@ const createStore = (invalidate, advance) => {
1260
1177
  }));
1261
1178
 
1262
1179
  return {
1180
+ set,
1181
+ get,
1263
1182
  // Mock objects that have to be configured
1264
1183
  gl: null,
1265
1184
  camera: null,
1266
1185
  raycaster: null,
1267
1186
  events: {
1187
+ priority: 1,
1188
+ enabled: true,
1268
1189
  connected: false
1269
1190
  },
1270
1191
  xr: null,
1271
- set,
1272
- get,
1273
1192
  invalidate: () => invalidate(get()),
1274
1193
  advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1275
1194
  linear: false,
@@ -1277,7 +1196,7 @@ const createStore = (invalidate, advance) => {
1277
1196
  scene: prepare(new THREE__namespace.Scene()),
1278
1197
  controls: null,
1279
1198
  clock: new THREE__namespace.Clock(),
1280
- mouse: new THREE__namespace.Vector2(),
1199
+ pointer: new THREE__namespace.Vector2(),
1281
1200
  frameloop: 'always',
1282
1201
  onPointerMissed: undefined,
1283
1202
  performance: {
@@ -1346,6 +1265,7 @@ const createStore = (invalidate, advance) => {
1346
1265
  frameloop
1347
1266
  }));
1348
1267
  },
1268
+ previousRoot: undefined,
1349
1269
  internal: {
1350
1270
  active: false,
1351
1271
  priority: 0,
@@ -1573,6 +1493,11 @@ function loadingFn(extensions, onProgress) {
1573
1493
  };
1574
1494
  }
1575
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
+ }
1576
1501
  function useLoader(Proto, input, extensions, onProgress) {
1577
1502
  // Use suspense to load async assets
1578
1503
  const keys = Array.isArray(input) ? input : [input];
@@ -1672,9 +1597,7 @@ function createRoot(canvas) {
1672
1597
  params,
1673
1598
  ...options
1674
1599
  } = raycastOptions || {};
1675
- if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, {
1676
- enabled: true,
1677
- ...options
1600
+ if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options
1678
1601
  });
1679
1602
  if (!is.equ(params, raycaster.params, shallowLoose)) applyProps(raycaster, {
1680
1603
  params: { ...raycaster.params,
@@ -1710,7 +1633,7 @@ function createRoot(canvas) {
1710
1633
 
1711
1634
  const handleSessionChange = () => {
1712
1635
  const gl = store.getState().gl;
1713
- gl.xr.enabled = gl.xr.isPresenting; // @ts-expect-error
1636
+ gl.xr.enabled = gl.xr.isPresenting; // @ts-ignore
1714
1637
  // WebXRManager's signature is incorrect.
1715
1638
  // See: https://github.com/pmndrs/react-three-fiber/pull/2017#discussion_r790134505
1716
1639
 
@@ -1752,6 +1675,7 @@ function createRoot(canvas) {
1752
1675
  } // Set color management
1753
1676
 
1754
1677
 
1678
+ if (THREE__namespace.ColorManagement) THREE__namespace.ColorManagement.legacyMode = false;
1755
1679
  const outputEncoding = linear ? THREE__namespace.LinearEncoding : THREE__namespace.sRGBEncoding;
1756
1680
  const toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
1757
1681
  if (gl.outputEncoding !== outputEncoding) gl.outputEncoding = outputEncoding;
@@ -1788,14 +1712,14 @@ function createRoot(canvas) {
1788
1712
  return this;
1789
1713
  },
1790
1714
 
1791
- render(element) {
1715
+ render(children) {
1792
1716
  // The root has to be configured before it can be rendered
1793
1717
  if (!configured) this.configure();
1794
1718
  reconciler.updateContainer( /*#__PURE__*/React__namespace.createElement(Provider, {
1795
1719
  store: store,
1796
- element: element,
1720
+ children: children,
1797
1721
  onCreated: onCreated,
1798
- target: canvas
1722
+ rootElement: canvas
1799
1723
  }), fiber, null, () => undefined);
1800
1724
  return store;
1801
1725
  },
@@ -1807,35 +1731,36 @@ function createRoot(canvas) {
1807
1731
  };
1808
1732
  }
1809
1733
 
1810
- function render(element, canvas, config = {}) {
1734
+ function render(children, canvas, config) {
1811
1735
  console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
1812
1736
  const root = createRoot(canvas);
1813
1737
  root.configure(config);
1814
- return root.render(element);
1738
+ return root.render(children);
1815
1739
  }
1816
1740
 
1817
1741
  function Provider({
1818
1742
  store,
1819
- element,
1743
+ children,
1820
1744
  onCreated,
1821
- target
1745
+ rootElement
1822
1746
  }) {
1823
- React__namespace.useEffect(() => {
1747
+ React__namespace.useLayoutEffect(() => {
1824
1748
  const state = store.getState(); // Flag the canvas active, rendering will now begin
1825
1749
 
1826
1750
  state.set(state => ({
1827
1751
  internal: { ...state.internal,
1828
1752
  active: true
1829
1753
  }
1830
- })); // Connect events
1754
+ })); // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
1831
1755
 
1832
- state.events.connect == null ? void 0 : state.events.connect(target); // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
1756
+ if (onCreated) onCreated(state); // Connect events to the targets parent, this is done to ensure events are registered on
1757
+ // a shared target, and not on the canvas itself
1833
1758
 
1834
- if (onCreated) onCreated(state); // eslint-disable-next-line react-hooks/exhaustive-deps
1759
+ if (!store.getState().events.connected) state.events.connect == null ? void 0 : state.events.connect(rootElement); // eslint-disable-next-line react-hooks/exhaustive-deps
1835
1760
  }, []);
1836
1761
  return /*#__PURE__*/React__namespace.createElement(context.Provider, {
1837
1762
  value: store
1838
- }, element);
1763
+ }, children);
1839
1764
  }
1840
1765
 
1841
1766
  function unmountComponentAtNode(canvas, callback) {
@@ -1867,10 +1792,67 @@ function unmountComponentAtNode(canvas, callback) {
1867
1792
  }
1868
1793
  }
1869
1794
 
1870
- const act = React__namespace.unstable_act;
1795
+ function createPortal(children, container, state) {
1796
+ return /*#__PURE__*/React__namespace.createElement(Portal, {
1797
+ children: children,
1798
+ container: container,
1799
+ state: state
1800
+ });
1801
+ }
1871
1802
 
1872
- function createPortal(children, container) {
1873
- return reconciler.createPortal(children, container, null, null);
1803
+ function Portal({
1804
+ state = {},
1805
+ children,
1806
+ container
1807
+ }) {
1808
+ /** This has to be a component because it would not be able to call useThree/useStore otherwise since
1809
+ * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
1810
+ * the "R3F hooks can only be used within the Canvas component!" warning:
1811
+ * <Canvas>
1812
+ * {createPortal(...)} */
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 ( // Some props should be off-limits
1827
+ !['size', 'viewport', 'internal', 'performance'].includes(key) && // Otherwise filter out the props that are different and let the inject layer take precedence
1828
+ state[key] !== injectState[key]) delete intersect[key];
1829
+ });
1830
+ }
1831
+
1832
+ return { ...intersect,
1833
+ scene: container,
1834
+ previousRoot,
1835
+ raycaster,
1836
+ events: { ...state.events,
1837
+ ...events
1838
+ },
1839
+ ...rest
1840
+ };
1841
+ }, [state]);
1842
+ const [useInjectStore] = React__namespace.useState(() => {
1843
+ const store = create__default['default']((set, get) => ({ ...inject(previousRoot.getState()),
1844
+ set,
1845
+ get
1846
+ }));
1847
+ previousRoot.subscribe(state => useInjectStore.setState(injectState => inject(state, injectState)));
1848
+ return store;
1849
+ });
1850
+ React__namespace.useEffect(() => {
1851
+ useInjectStore.setState(injectState => inject(previousRoot.getState(), injectState));
1852
+ }, [inject]);
1853
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, reconciler.createPortal( /*#__PURE__*/React__namespace.createElement(context.Provider, {
1854
+ value: useInjectStore
1855
+ }, children), useInjectStore, null));
1874
1856
  }
1875
1857
 
1876
1858
  reconciler.injectIntoDevTools({
@@ -1878,6 +1860,7 @@ reconciler.injectIntoDevTools({
1878
1860
  rendererPackageName: '@react-three/fiber',
1879
1861
  version: '18.0.0'
1880
1862
  });
1863
+ const act = React__namespace.unstable_act;
1881
1864
 
1882
1865
  exports.act = act;
1883
1866
  exports.addAfterEffect = addAfterEffect;
@@ -1891,6 +1874,7 @@ exports.createPortal = createPortal;
1891
1874
  exports.createRoot = createRoot;
1892
1875
  exports.dispose = dispose;
1893
1876
  exports.extend = extend;
1877
+ exports.getRootState = getRootState;
1894
1878
  exports.invalidate = invalidate;
1895
1879
  exports.omit = omit;
1896
1880
  exports.pick = pick;
@@ -1902,5 +1886,6 @@ exports.unmountComponentAtNode = unmountComponentAtNode;
1902
1886
  exports.useFrame = useFrame;
1903
1887
  exports.useGraph = useGraph;
1904
1888
  exports.useLoader = useLoader;
1889
+ exports.useMemoizedFn = useMemoizedFn;
1905
1890
  exports.useStore = useStore;
1906
1891
  exports.useThree = useThree;