@react-three/fiber 8.0.0-beta-01 → 8.0.0-beta-05

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,9 +1,10 @@
1
- import * as THREE from 'three';
2
- import Reconciler from 'react-reconciler';
3
- import { DefaultEventPriority, IdleEventPriority } from 'react-reconciler/constants';
4
1
  import * as React from 'react';
5
2
  import { suspend, preload, clear } from 'suspend-react';
3
+ import * as THREE from 'three';
4
+ import { DefaultEventPriority, ContinuousEventPriority, DiscreteEventPriority, ConcurrentRoot } from 'react-reconciler/constants';
6
5
  import create from 'zustand';
6
+ import Reconciler from 'react-reconciler';
7
+ import { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
7
8
 
8
9
  var threeTypes = /*#__PURE__*/Object.freeze({
9
10
  __proto__: null
@@ -13,7 +14,34 @@ const DEFAULT = '__default';
13
14
  const isDiffSet = def => def && !!def.memoized && !!def.changes;
14
15
  function calculateDpr(dpr) {
15
16
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
16
- } // A collection of compare functions
17
+ }
18
+ /**
19
+ * Picks or omits keys from an object
20
+ * `omit` will filter out keys, and otherwise cherry-pick them.
21
+ */
22
+
23
+ function filterKeys(obj, omit, ...keys) {
24
+ const keysToSelect = new Set(keys);
25
+ return Object.entries(obj).reduce((acc, [key, value]) => {
26
+ const shouldInclude = !omit;
27
+
28
+ if (keysToSelect.has(key) === shouldInclude) {
29
+ acc[key] = value;
30
+ }
31
+
32
+ return acc;
33
+ }, {});
34
+ }
35
+ /**
36
+ * Clones an object and cherry-picks keys.
37
+ */
38
+
39
+ const pick = (obj, keys) => filterKeys(obj, false, ...keys);
40
+ /**
41
+ * Clones an object and prunes or omits keys.
42
+ */
43
+
44
+ const omit = (obj, keys) => filterKeys(obj, true, ...keys); // A collection of compare functions
17
45
 
18
46
  const is = {
19
47
  obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
@@ -62,8 +90,7 @@ function dispose(obj) {
62
90
  if (obj.dispose && obj.type !== 'Scene') obj.dispose();
63
91
 
64
92
  for (const p in obj) {
65
- var _dispose, _ref;
66
- (_dispose = (_ref = p).dispose) == null ? void 0 : _dispose.call(_ref);
93
+ p.dispose == null ? void 0 : p.dispose();
67
94
  delete obj[p];
68
95
  }
69
96
  } // Each object in the scene carries a small LocalState descriptor
@@ -84,6 +111,49 @@ function prepare(object, state) {
84
111
  }
85
112
 
86
113
  return object;
114
+ }
115
+
116
+ function resolve(instance, key) {
117
+ let target = instance;
118
+
119
+ if (key.includes('-')) {
120
+ const entries = key.split('-');
121
+ const last = entries.pop();
122
+ target = entries.reduce((acc, key) => acc[key], instance);
123
+ return {
124
+ target,
125
+ key: last
126
+ };
127
+ } else return {
128
+ target,
129
+ key
130
+ };
131
+ }
132
+
133
+ function attach(parent, child, type) {
134
+ if (is.str(type)) {
135
+ const {
136
+ target,
137
+ key
138
+ } = resolve(parent, type);
139
+ parent.__r3f.previousAttach = target[key];
140
+ target[key] = child;
141
+ } else if (is.arr(type)) {
142
+ const [attach] = type;
143
+ if (is.str(attach)) parent[attach](child);else if (is.fun(attach)) attach(parent, child);
144
+ }
145
+ }
146
+ function detach(parent, child, type) {
147
+ if (is.str(type)) {
148
+ const {
149
+ target,
150
+ key
151
+ } = resolve(parent, type);
152
+ target[key] = parent.__r3f.previousAttach;
153
+ } else if (is.arr(type)) {
154
+ const [, detach] = type;
155
+ if (is.str(detach)) parent[detach](child);else if (is.fun(detach)) detach(parent, child);
156
+ }
87
157
  } // Shallow check arrays, but check objects atomically
88
158
 
89
159
  function checkShallow(a, b) {
@@ -113,7 +183,9 @@ function diffProps(instance, {
113
183
  if (remove) {
114
184
  const previousKeys = Object.keys(previous);
115
185
 
116
- for (let i = 0; i < previousKeys.length; i++) if (!props.hasOwnProperty(previousKeys[i])) entries.unshift([previousKeys[i], DEFAULT + 'remove']);
186
+ for (let i = 0; i < previousKeys.length; i++) {
187
+ if (!props.hasOwnProperty(previousKeys[i])) entries.unshift([previousKeys[i], DEFAULT + 'remove']);
188
+ }
117
189
  }
118
190
 
119
191
  entries.forEach(([key, value]) => {
@@ -140,7 +212,7 @@ function diffProps(instance, {
140
212
  };
141
213
  } // This function applies a set of changes to the instance
142
214
 
143
- function applyProps(instance, data) {
215
+ function applyProps$1(instance, data) {
144
216
  var _instance$__r3f3, _root$getState;
145
217
 
146
218
  // Filter equals, events and reserved props
@@ -184,7 +256,9 @@ function applyProps(instance, data) {
184
256
  value = defaultClassCall[targetProp]; // destory the instance
185
257
 
186
258
  if (defaultClassCall.dispose) defaultClassCall.dispose(); // instance does not have constructor, just set it to 0
187
- } else value = 0;
259
+ } else {
260
+ value = 0;
261
+ }
188
262
  } // Deal with pointer events ...
189
263
 
190
264
 
@@ -193,35 +267,39 @@ function applyProps(instance, data) {
193
267
  localState.eventCount = Object.keys(localState.handlers).length;
194
268
  } // Special treatment for objects with support for set/copy, and layers
195
269
  else if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE.Layers)) {
196
- // If value is an array
197
- if (Array.isArray(value)) {
198
- if (targetProp.fromArray) targetProp.fromArray(value);else targetProp.set(...value);
199
- } // Test again target.copy(class) next ...
200
- else if (targetProp.copy && value && value.constructor && targetProp.constructor.name === value.constructor.name) targetProp.copy(value); // If nothing else fits, just set the single value, ignore undefined
201
- // https://github.com/react-spring/react-three-fiber/issues/274
202
- else if (value !== undefined) {
203
- const isColor = targetProp instanceof THREE.Color; // Allow setting array scalars
204
-
205
- if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
206
- else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) targetProp.mask = value.mask; // Otherwise just set ...
207
- else targetProp.set(value); // Auto-convert sRGB colors, for now ...
208
- // https://github.com/react-spring/react-three-fiber/issues/344
209
-
210
- if (!rootState.linear && isColor) targetProp.convertSRGBToLinear();
211
- } // Else, just overwrite the value
270
+ // If value is an array
271
+ if (Array.isArray(value)) {
272
+ if (targetProp.fromArray) targetProp.fromArray(value);else targetProp.set(...value);
273
+ } // Test again target.copy(class) next ...
274
+ else if (targetProp.copy && value && value.constructor && targetProp.constructor.name === value.constructor.name) {
275
+ targetProp.copy(value);
276
+ } // If nothing else fits, just set the single value, ignore undefined
277
+ // https://github.com/pmndrs/react-three-fiber/issues/274
278
+ else if (value !== undefined) {
279
+ const isColor = targetProp instanceof THREE.Color; // Allow setting array scalars
280
+
281
+ if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
282
+ else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) targetProp.mask = value.mask; // Otherwise just set ...
283
+ else targetProp.set(value); // Auto-convert sRGB colors, for now ...
284
+ // https://github.com/pmndrs/react-three-fiber/issues/344
285
+
286
+ if (!rootState.linear && isColor) targetProp.convertSRGBToLinear();
287
+ } // Else, just overwrite the value
212
288
 
213
- } else {
214
- currentInstance[key] = value; // Auto-convert sRGB textures, for now ...
215
- // https://github.com/react-spring/react-three-fiber/issues/344
289
+ } else {
290
+ currentInstance[key] = value; // Auto-convert sRGB textures, for now ...
291
+ // https://github.com/pmndrs/react-three-fiber/issues/344
216
292
 
217
- if (!rootState.linear && currentInstance[key] instanceof THREE.Texture) currentInstance[key].encoding = THREE.sRGBEncoding;
218
- }
293
+ if (!rootState.linear && currentInstance[key] instanceof THREE.Texture) {
294
+ currentInstance[key].encoding = THREE.sRGBEncoding;
295
+ }
296
+ }
219
297
 
220
298
  invalidateInstance(instance);
221
299
  return instance;
222
300
  });
223
301
 
224
- if (rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
302
+ if (localState.parent && rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
225
303
  // Pre-emptively remove the instance from the interaction manager
226
304
  const index = rootState.internal.interaction.indexOf(instance);
227
305
  if (index > -1) rootState.internal.interaction.splice(index, 1); // Add the instance to the interaction manager only when it has handlers
@@ -244,12 +322,41 @@ function updateInstance(instance) {
244
322
 
245
323
  function makeId(event) {
246
324
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
325
+ } // https://github.com/facebook/react/tree/main/packages/react-reconciler#getcurrenteventpriority
326
+ // Gives React a clue as to how import the current interaction is
327
+
328
+
329
+ function getEventPriority() {
330
+ var _window, _window$event;
331
+
332
+ let name = (_window = window) == null ? void 0 : (_window$event = _window.event) == null ? void 0 : _window$event.type;
333
+
334
+ switch (name) {
335
+ case 'click':
336
+ case 'contextmenu':
337
+ case 'dblclick':
338
+ case 'pointercancel':
339
+ case 'pointerdown':
340
+ case 'pointerup':
341
+ return DiscreteEventPriority;
342
+
343
+ case 'pointermove':
344
+ case 'pointerout':
345
+ case 'pointerover':
346
+ case 'pointerenter':
347
+ case 'pointerleave':
348
+ case 'wheel':
349
+ return ContinuousEventPriority;
350
+
351
+ default:
352
+ return DefaultEventPriority;
353
+ }
247
354
  }
248
- /** Release pointer captures.
355
+ /**
356
+ * Release pointer captures.
249
357
  * This is called by releasePointerCapture in the API, and when an object is removed.
250
358
  */
251
359
 
252
-
253
360
  function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
254
361
  const captureData = captures.get(obj);
255
362
 
@@ -284,7 +391,7 @@ function createEvents(store) {
284
391
  /** Sets up defaultRaycaster */
285
392
 
286
393
  function prepareRay(event) {
287
- var _raycaster$computeOff;
394
+ var _customOffsets$offset, _customOffsets$offset2, _customOffsets$width, _customOffsets$height;
288
395
 
289
396
  const state = store.getState();
290
397
  const {
@@ -295,14 +402,11 @@ function createEvents(store) {
295
402
  } = state; // https://github.com/pmndrs/react-three-fiber/pull/782
296
403
  // Events trigger outside of canvas when moved
297
404
 
298
- const {
299
- offsetX,
300
- offsetY
301
- } = (_raycaster$computeOff = raycaster.computeOffsets == null ? void 0 : raycaster.computeOffsets(event, state)) != null ? _raycaster$computeOff : event;
302
- const {
303
- width,
304
- height
305
- } = size;
405
+ const customOffsets = raycaster.computeOffsets == null ? void 0 : raycaster.computeOffsets(event, state);
406
+ const offsetX = (_customOffsets$offset = customOffsets == null ? void 0 : customOffsets.offsetX) != null ? _customOffsets$offset : event.offsetX;
407
+ const offsetY = (_customOffsets$offset2 = customOffsets == null ? void 0 : customOffsets.offsetY) != null ? _customOffsets$offset2 : event.offsetY;
408
+ const width = (_customOffsets$width = customOffsets == null ? void 0 : customOffsets.width) != null ? _customOffsets$width : size.width;
409
+ const height = (_customOffsets$height = customOffsets == null ? void 0 : customOffsets.height) != null ? _customOffsets$height : size.height;
306
410
  mouse.set(offsetX / width * 2 - 1, -(offsetY / height) * 2 + 1);
307
411
  raycaster.setFromCamera(mouse, camera);
308
412
  }
@@ -532,13 +636,16 @@ function createEvents(store) {
532
636
 
533
637
  case 'onLostPointerCapture':
534
638
  return event => {
535
- if ('pointerId' in event) {
639
+ const {
640
+ internal
641
+ } = store.getState();
642
+
643
+ if ('pointerId' in event && !internal.capturedMap.has(event.pointerId)) {
536
644
  // If the object event interface had onLostPointerCapture, we'd call it here on every
537
645
  // object that's getting removed.
538
- store.getState().internal.capturedMap.delete(event.pointerId);
646
+ internal.capturedMap.delete(event.pointerId);
647
+ cancelPointer([]);
539
648
  }
540
-
541
- cancelPointer([]);
542
649
  };
543
650
  } // Any other pointer goes here ...
544
651
 
@@ -548,7 +655,8 @@ function createEvents(store) {
548
655
  onPointerMissed,
549
656
  internal
550
657
  } = store.getState();
551
- prepareRay(event); // Get fresh intersects
658
+ prepareRay(event);
659
+ internal.lastEvent.current = event; // Get fresh intersects
552
660
 
553
661
  const isPointerMove = name === 'onPointerMove';
554
662
  const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
@@ -606,12 +714,17 @@ function createEvents(store) {
606
714
  if (handler) {
607
715
  // Forward all events back to their respective handlers with the exception of click events,
608
716
  // which must use the initial target
609
- if (name !== 'onClick' && name !== 'onContextMenu' && name !== 'onDoubleClick' || internal.initialHits.includes(eventObject)) {
717
+ if (!isClickEvent || internal.initialHits.includes(eventObject)) {
610
718
  // Missed events have to come first
611
719
  pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object))); // Now call the handler
612
720
 
613
721
  handler(data);
614
722
  }
723
+ } else {
724
+ // Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
725
+ if (isClickEvent && internal.initialHits.includes(eventObject)) {
726
+ pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
727
+ }
615
728
  }
616
729
  }
617
730
  });
@@ -657,6 +770,7 @@ let extend = objects => void (catalogue = { ...catalogue,
657
770
  function createRenderer(roots, getEventPriority) {
658
771
  function createInstance(type, {
659
772
  args = [],
773
+ attach,
660
774
  ...props
661
775
  }, root, hostContext, internalInstanceHandle) {
662
776
  let name = `${type[0].toUpperCase()}${type.slice(1)}`;
@@ -673,87 +787,65 @@ function createRenderer(roots, getEventPriority) {
673
787
  } // Assert that by now we have a valid root
674
788
 
675
789
 
676
- if (!root || !isStore(root)) throw `No valid root for ${name}!`;
790
+ if (!root || !isStore(root)) throw `No valid root for ${name}!`; // Auto-attach geometries and materials
791
+
792
+ if (attach === undefined) {
793
+ if (name.endsWith('Geometry')) attach = 'geometry';else if (name.endsWith('Material')) attach = 'material';
794
+ }
677
795
 
678
796
  if (type === 'primitive') {
679
797
  if (props.object === undefined) throw `Primitives without 'object' are invalid!`;
680
798
  const object = props.object;
681
799
  instance = prepare(object, {
682
800
  root,
801
+ attach,
683
802
  primitive: true
684
803
  });
685
804
  } else {
686
- const target = catalogue[name] || THREE[name];
687
- if (!target) throw `${name} is not part of the THREE namespace! Did you forget to extend? See: https://github.com/pmndrs/react-three-fiber/blob/master/markdown/api.md#using-3rd-party-objects-declaratively`; // Instanciate new object, link it to the root
805
+ const target = catalogue[name];
806
+
807
+ if (!target) {
808
+ throw `${name} is not part of the THREE namespace! Did you forget to extend? See: https://github.com/pmndrs/react-three-fiber/blob/master/markdown/api.md#using-3rd-party-objects-declaratively`;
809
+ } // Throw if an object or literal was passed for args
810
+
811
+
812
+ if (!Array.isArray(args)) throw 'The args prop must be an array!'; // Instanciate new object, link it to the root
688
813
  // Append memoized props with args so it's not forgotten
689
814
 
690
815
  instance = prepare(new target(...args), {
691
816
  root,
817
+ attach,
818
+ // TODO: Figure out what this is for
692
819
  memoizedProps: {
693
820
  args: args.length === 0 ? null : args
694
821
  }
695
822
  });
696
- } // Auto-attach geometries and materials
697
-
698
-
699
- if (!('attachFns' in props)) {
700
- if (name.endsWith('Geometry')) {
701
- props = {
702
- attach: 'geometry',
703
- ...props
704
- };
705
- } else if (name.endsWith('Material')) {
706
- props = {
707
- attach: 'material',
708
- ...props
709
- };
710
- }
711
823
  } // It should NOT call onUpdate on object instanciation, because it hasn't been added to the
712
824
  // view yet. If the callback relies on references for instance, they won't be ready yet, this is
713
825
  // why it passes "true" here
714
826
 
715
827
 
716
- applyProps(instance, props);
828
+ applyProps$1(instance, props);
717
829
  return instance;
718
830
  }
719
831
 
720
832
  function appendChild(parentInstance, child) {
721
- let addedAsChild = false;
833
+ let added = false;
722
834
 
723
835
  if (child) {
724
836
  // The attach attribute implies that the object attaches itself on the parent
725
- if (child.attachArray) {
726
- if (!is.arr(parentInstance[child.attachArray])) parentInstance[child.attachArray] = [];
727
- parentInstance[child.attachArray].push(child);
728
- } else if (child.attachObject) {
729
- if (!is.obj(parentInstance[child.attachObject[0]])) parentInstance[child.attachObject[0]] = {};
730
- parentInstance[child.attachObject[0]][child.attachObject[1]] = child;
731
- } else if (child.attach && !is.fun(child.attach)) {
732
- parentInstance[child.attach] = child;
733
- } else if (is.arr(child.attachFns)) {
734
- const [attachFn] = child.attachFns;
735
-
736
- if (is.str(attachFn) && is.fun(parentInstance[attachFn])) {
737
- parentInstance[attachFn](child);
738
- } else if (is.fun(attachFn)) {
739
- attachFn(child, parentInstance);
740
- }
837
+ if (child.__r3f.attach) {
838
+ attach(parentInstance, child, child.__r3f.attach);
741
839
  } else if (child.isObject3D && parentInstance.isObject3D) {
742
840
  // add in the usual parent-child way
743
841
  parentInstance.add(child);
744
- addedAsChild = true;
745
- }
746
-
747
- if (!addedAsChild) {
748
- // This is for anything that used attach, and for non-Object3Ds that don't get attached to props;
749
- // that is, anything that's a child in React but not a child in the scenegraph.
750
- parentInstance.__r3f.objects.push(child);
751
- }
842
+ added = true;
843
+ } // This is for anything that used attach, and for non-Object3Ds that don't get attached to props;
844
+ // that is, anything that's a child in React but not a child in the scenegraph.
752
845
 
753
- if (!child.__r3f) {
754
- prepare(child, {});
755
- }
756
846
 
847
+ if (!added) parentInstance.__r3f.objects.push(child);
848
+ if (!child.__r3f) prepare(child, {});
757
849
  child.__r3f.parent = parentInstance;
758
850
  updateInstance(child);
759
851
  invalidateInstance(child);
@@ -764,13 +856,8 @@ function createRenderer(roots, getEventPriority) {
764
856
  let added = false;
765
857
 
766
858
  if (child) {
767
- if (child.attachArray) {
768
- const array = parentInstance[child.attachArray];
769
- if (!is.arr(array)) parentInstance[child.attachArray] = [];
770
- array.splice(array.indexOf(beforeChild), 0, child);
771
- } else if (child.attachObject || child.attach && !is.fun(child.attach)) {
772
- // attach and attachObject don't have an order anyway, so just append
773
- return appendChild(parentInstance, child);
859
+ if (child.__r3f.attach) {
860
+ attach(parentInstance, child, child.__r3f.attach);
774
861
  } else if (child.isObject3D && parentInstance.isObject3D) {
775
862
  child.parent = parentInstance;
776
863
  child.dispatchEvent({
@@ -782,14 +869,8 @@ function createRenderer(roots, getEventPriority) {
782
869
  added = true;
783
870
  }
784
871
 
785
- if (!added) {
786
- parentInstance.__r3f.objects.push(child);
787
- }
788
-
789
- if (!child.__r3f) {
790
- prepare(child, {});
791
- }
792
-
872
+ if (!added) parentInstance.__r3f.objects.push(child);
873
+ if (!child.__r3f) prepare(child, {});
793
874
  child.__r3f.parent = parentInstance;
794
875
  updateInstance(child);
795
876
  invalidateInstance(child);
@@ -804,29 +885,13 @@ function createRenderer(roots, getEventPriority) {
804
885
  if (child) {
805
886
  var _parentInstance$__r3f, _child$__r3f2;
806
887
 
807
- if (child.__r3f) {
808
- child.__r3f.parent = null;
809
- }
810
-
811
- if ((_parentInstance$__r3f = parentInstance.__r3f) != null && _parentInstance$__r3f.objects) {
812
- parentInstance.__r3f.objects = parentInstance.__r3f.objects.filter(x => x !== child);
813
- } // Remove attachment
888
+ // Clear the parent reference
889
+ if (child.__r3f) child.__r3f.parent = null; // Remove child from the parents objects
814
890
 
891
+ if ((_parentInstance$__r3f = parentInstance.__r3f) != null && _parentInstance$__r3f.objects) parentInstance.__r3f.objects = parentInstance.__r3f.objects.filter(x => x !== child); // Remove attachment
815
892
 
816
- if (child.attachArray) {
817
- parentInstance[child.attachArray] = parentInstance[child.attachArray].filter(x => x !== child);
818
- } else if (child.attachObject) {
819
- delete parentInstance[child.attachObject[0]][child.attachObject[1]];
820
- } else if (child.attach && !is.fun(child.attach)) {
821
- parentInstance[child.attach] = null;
822
- } else if (is.arr(child.attachFns)) {
823
- const [, detachFn] = child.attachFns;
824
-
825
- if (is.str(detachFn) && is.fun(parentInstance[detachFn])) {
826
- parentInstance[detachFn](child);
827
- } else if (is.fun(detachFn)) {
828
- detachFn(child, parentInstance);
829
- }
893
+ if (child.__r3f.attach) {
894
+ detach(parentInstance, child, child.__r3f.attach);
830
895
  } else if (child.isObject3D && parentInstance.isObject3D) {
831
896
  var _child$__r3f;
832
897
 
@@ -868,7 +933,7 @@ function createRenderer(roots, getEventPriority) {
868
933
 
869
934
 
870
935
  if (shouldDispose && child.dispose && child.type !== 'Scene') {
871
- reconciler.runWithPriority(IdleEventPriority, () => {
936
+ unstable_scheduleCallback(unstable_IdlePriority, () => {
872
937
  try {
873
938
  child.dispose();
874
939
  } catch (e) {
@@ -900,10 +965,13 @@ function createRenderer(roots, getEventPriority) {
900
965
 
901
966
  instance.__r3f.objects = [];
902
967
  removeChild(parent, instance);
903
- appendChild(parent, newInstance) // This evil hack switches the react-internal fiber node
904
- // https://github.com/facebook/react/issues/14983
905
- // https://github.com/facebook/react/pull/15021
906
- ;
968
+ appendChild(parent, newInstance); // Re-bind event handlers
969
+
970
+ if (newInstance.raycast && newInstance.__r3f.eventCount) {
971
+ const rootState = newInstance.__r3f.root.getState();
972
+
973
+ rootState.internal.interaction.push(newInstance);
974
+ } // This evil hack switches the react-internal fiber node
907
975
  [fiber, fiber.alternate].forEach(fiber => {
908
976
  if (fiber !== null) {
909
977
  fiber.stateNode = newInstance;
@@ -929,7 +997,9 @@ function createRenderer(roots, getEventPriority) {
929
997
  insertInContainerBefore: (parentInstance, child, beforeChild) => insertBefore(getContainer(parentInstance, child).container, child, beforeChild),
930
998
 
931
999
  prepareUpdate(instance, type, oldProps, newProps) {
932
- if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) return [true];else {
1000
+ if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) {
1001
+ return [true];
1002
+ } else {
933
1003
  // This is a data object, let's extract critical information about it
934
1004
  const {
935
1005
  args: argsNew = [],
@@ -940,7 +1010,9 @@ function createRenderer(roots, getEventPriority) {
940
1010
  args: argsOld = [],
941
1011
  children: cO,
942
1012
  ...restOld
943
- } = oldProps; // If it has new props or arguments, then it needs to be re-instanciated
1013
+ } = oldProps; // Throw if an object or literal was passed for args
1014
+
1015
+ if (!Array.isArray(argsNew)) throw 'The args prop must be an array!'; // If it has new props or arguments, then it needs to be re-instanciated
944
1016
 
945
1017
  if (argsNew.some((value, index) => value !== argsOld[index])) return [true]; // Create a diff-set, flag if there are any changes
946
1018
 
@@ -954,21 +1026,33 @@ function createRenderer(roots, getEventPriority) {
954
1026
  commitUpdate(instance, [reconstruct, diff], type, oldProps, newProps, fiber) {
955
1027
  // Reconstruct when args or <primitive object={...} have changes
956
1028
  if (reconstruct) switchInstance(instance, type, newProps, fiber); // Otherwise just overwrite props
957
- else applyProps(instance, diff);
1029
+ else applyProps$1(instance, diff);
958
1030
  },
959
1031
 
960
1032
  hideInstance(instance) {
961
- if (instance.isObject3D) {
962
- instance.visible = false;
963
- invalidateInstance(instance);
964
- }
1033
+ var _instance$__r3f2;
1034
+
1035
+ // Deatch while the instance is hidden
1036
+ const {
1037
+ attach: type,
1038
+ parent
1039
+ } = (_instance$__r3f2 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f2 : {};
1040
+ if (type && parent) detach(parent, instance, type);
1041
+ if (instance.isObject3D) instance.visible = false;
1042
+ invalidateInstance(instance);
965
1043
  },
966
1044
 
967
1045
  unhideInstance(instance, props) {
968
- if (instance.isObject3D && props.visible == null || props.visible) {
969
- instance.visible = true;
970
- invalidateInstance(instance);
971
- }
1046
+ var _instance$__r3f3;
1047
+
1048
+ // Re-attach when the instance is unhidden
1049
+ const {
1050
+ attach: type,
1051
+ parent
1052
+ } = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
1053
+ if (type && parent) attach(parent, instance, type);
1054
+ if (instance.isObject3D && props.visible == null || props.visible) instance.visible = true;
1055
+ invalidateInstance(instance);
972
1056
  },
973
1057
 
974
1058
  createInstance,
@@ -981,7 +1065,7 @@ function createRenderer(roots, getEventPriority) {
981
1065
  isPrimaryRenderer: false,
982
1066
  getCurrentEventPriority: () => getEventPriority ? getEventPriority() : DefaultEventPriority,
983
1067
  // @ts-ignore
984
- now: is.fun(performance.now) ? performance.now : is.fun(Date.now) ? Date.now : undefined,
1068
+ now: typeof performance !== 'undefined' && is.fun(performance.now) ? performance.now : is.fun(Date.now) ? Date.now : undefined,
985
1069
  // @ts-ignore
986
1070
  scheduleTimeout: is.fun(setTimeout) ? setTimeout : undefined,
987
1071
  // @ts-ignore
@@ -997,8 +1081,30 @@ function createRenderer(roots, getEventPriority) {
997
1081
  getRootHostContext: () => null,
998
1082
  getChildHostContext: parentHostContext => parentHostContext,
999
1083
  createTextInstance: () => {},
1000
- finalizeInitialChildren: () => false,
1001
- commitMount: () => {},
1084
+
1085
+ finalizeInitialChildren(instance) {
1086
+ var _instance$__r3f4;
1087
+
1088
+ // https://github.com/facebook/react/issues/20271
1089
+ // Returning true will trigger commitMount
1090
+ const localState = (_instance$__r3f4 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f4 : {};
1091
+ return !!localState.handlers;
1092
+ },
1093
+
1094
+ commitMount(instance)
1095
+ /*, type, props*/
1096
+ {
1097
+ var _instance$__r3f5;
1098
+
1099
+ // https://github.com/facebook/react/issues/20271
1100
+ // This will make sure events are only added once to the central container
1101
+ const localState = (_instance$__r3f5 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f5 : {};
1102
+
1103
+ if (instance.raycast && localState.handlers && localState.eventCount) {
1104
+ instance.__r3f.root.getState().internal.interaction.push(instance);
1105
+ }
1106
+ },
1107
+
1002
1108
  shouldDeprioritizeSubtree: () => false,
1003
1109
  prepareForCommit: () => null,
1004
1110
  preparePortalMount: containerInfo => prepare(containerInfo),
@@ -1009,7 +1115,7 @@ function createRenderer(roots, getEventPriority) {
1009
1115
  });
1010
1116
  return {
1011
1117
  reconciler,
1012
- applyProps
1118
+ applyProps: applyProps$1
1013
1119
  };
1014
1120
  }
1015
1121
 
@@ -1120,18 +1226,19 @@ const createStore = (applyProps, invalidate, advance, props) => {
1120
1226
  })); // Handle frame behavior in WebXR
1121
1227
 
1122
1228
 
1123
- const handleXRFrame = timestamp => {
1229
+ const handleXRFrame = (timestamp, frame) => {
1124
1230
  const state = get();
1125
1231
  if (state.frameloop === 'never') return;
1126
- advance(timestamp, true);
1232
+ advance(timestamp, true, state, frame);
1127
1233
  }; // Toggle render switching on session
1128
1234
 
1129
1235
 
1130
1236
  const handleSessionChange = () => {
1131
- gl.xr.enabled = gl.xr.isPresenting;
1132
- gl.setAnimationLoop(gl.xr.isPresenting ? handleXRFrame : null); // If exiting session, request frame
1237
+ gl.xr.enabled = gl.xr.isPresenting; // @ts-expect-error
1238
+ // WebXRManager's signature is incorrect.
1239
+ // See: https://github.com/pmndrs/react-three-fiber/pull/2017#discussion_r790134505
1133
1240
 
1134
- if (!gl.xr.isPresenting) invalidate(get());
1241
+ gl.xr.setAnimationLoop(gl.xr.isPresenting ? handleXRFrame : null);
1135
1242
  }; // WebXR session manager
1136
1243
 
1137
1244
 
@@ -1212,6 +1319,9 @@ const createStore = (applyProps, invalidate, advance, props) => {
1212
1319
  dpr: calculateDpr(dpr)
1213
1320
  }
1214
1321
  })),
1322
+ setFrameloop: (frameloop = 'always') => set(() => ({
1323
+ frameloop
1324
+ })),
1215
1325
  events: {
1216
1326
  connected: false
1217
1327
  },
@@ -1220,6 +1330,7 @@ const createStore = (applyProps, invalidate, advance, props) => {
1220
1330
  priority: 0,
1221
1331
  frames: 0,
1222
1332
  lastProps: props,
1333
+ lastEvent: /*#__PURE__*/React.createRef(),
1223
1334
  interaction: [],
1224
1335
  hovered: new Map(),
1225
1336
  subscribers: [],
@@ -1276,7 +1387,7 @@ const createStore = (applyProps, invalidate, advance, props) => {
1276
1387
  if (size !== oldSize || viewport.dpr !== oldDpr) {
1277
1388
  // https://github.com/pmndrs/react-three-fiber/issues/92
1278
1389
  // Do not mess with the camera if it belongs to the user
1279
- if (!(internal.lastProps.camera instanceof THREE.Camera)) {
1390
+ if (!camera.manual && !(internal.lastProps.camera instanceof THREE.Camera)) {
1280
1391
  if (isOrthographicCamera(camera)) {
1281
1392
  camera.left = size.width / -2;
1282
1393
  camera.right = size.width / 2;
@@ -1325,7 +1436,7 @@ function run(effects, timestamp) {
1325
1436
  for (i = 0; i < effects.length; i++) effects[i](timestamp);
1326
1437
  }
1327
1438
 
1328
- function render(timestamp, state) {
1439
+ function render$1(timestamp, state, frame) {
1329
1440
  // Run local effects
1330
1441
  let delta = state.clock.getDelta(); // In frameloop='never' mode, clock times are updated using the provided timestamp
1331
1442
 
@@ -1336,7 +1447,7 @@ function render(timestamp, state) {
1336
1447
  } // Call subscribers (useFrame)
1337
1448
 
1338
1449
 
1339
- for (i = 0; i < state.internal.subscribers.length; i++) state.internal.subscribers[i].ref.current(state, delta); // Render content
1450
+ for (i = 0; i < state.internal.subscribers.length; i++) state.internal.subscribers[i].ref.current(state, delta, frame); // Render content
1340
1451
 
1341
1452
 
1342
1453
  if (!state.internal.priority && state.gl.render) state.gl.render(state.scene, state.camera); // Decrease frame count
@@ -1360,7 +1471,9 @@ function createLoop(roots) {
1360
1471
 
1361
1472
  const state = root.store.getState(); // If the frameloop is invalidated, do not run another frame
1362
1473
 
1363
- if (state.internal.active && (state.frameloop === 'always' || state.internal.frames > 0) && !((_state$gl$xr = state.gl.xr) != null && _state$gl$xr.isPresenting)) repeat += render(timestamp, state);
1474
+ if (state.internal.active && (state.frameloop === 'always' || state.internal.frames > 0) && !((_state$gl$xr = state.gl.xr) != null && _state$gl$xr.isPresenting)) {
1475
+ repeat += render$1(timestamp, state);
1476
+ }
1364
1477
  }); // Run after-effects
1365
1478
 
1366
1479
  run(globalAfterEffects, timestamp); // Keep on looping if anything invalidates the frameloop
@@ -1385,9 +1498,9 @@ function createLoop(roots) {
1385
1498
  }
1386
1499
  }
1387
1500
 
1388
- function advance(timestamp, runGlobalEffects = true, state) {
1501
+ function advance(timestamp, runGlobalEffects = true, state, frame) {
1389
1502
  if (runGlobalEffects) run(globalEffects, timestamp);
1390
- if (!state) roots.forEach(root => render(timestamp, root.store.getState()));else render(timestamp, state);
1503
+ if (!state) roots.forEach(root => render$1(timestamp, root.store.getState()));else render$1(timestamp, state, frame);
1391
1504
  if (runGlobalEffects) run(globalAfterEffects, timestamp);
1392
1505
  }
1393
1506
 
@@ -1452,4 +1565,187 @@ useLoader.clear = function (Proto, input) {
1452
1565
  return clear([Proto, ...keys]);
1453
1566
  };
1454
1567
 
1455
- export { createLoop as a, createRenderer as b, createEvents as c, calculateDpr as d, createStore as e, context as f, dispose as g, extend as h, isRenderer as i, addEffect as j, addAfterEffect as k, addTail as l, useThree as m, useFrame as n, useGraph as o, useLoader as p, buildGraph as q, is as r, threeTypes as t, useStore as u };
1568
+ const roots = new Map();
1569
+ const {
1570
+ invalidate,
1571
+ advance
1572
+ } = createLoop(roots);
1573
+ const {
1574
+ reconciler,
1575
+ applyProps
1576
+ } = createRenderer(roots, getEventPriority);
1577
+
1578
+ const createRendererInstance = (gl, canvas) => {
1579
+ const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1580
+ if (isRenderer(customRenderer)) return customRenderer;
1581
+ const renderer = new THREE.WebGLRenderer({
1582
+ powerPreference: 'high-performance',
1583
+ canvas: canvas,
1584
+ antialias: true,
1585
+ alpha: true,
1586
+ ...gl
1587
+ }); // Set color management
1588
+
1589
+ renderer.outputEncoding = THREE.sRGBEncoding;
1590
+ renderer.toneMapping = THREE.ACESFilmicToneMapping; // Set gl props
1591
+
1592
+ if (gl) applyProps(renderer, gl);
1593
+ return renderer;
1594
+ };
1595
+
1596
+ function createRoot(canvas, config) {
1597
+ return {
1598
+ render: element => {
1599
+ var _store;
1600
+
1601
+ let {
1602
+ gl,
1603
+ size,
1604
+ events,
1605
+ onCreated,
1606
+ ...props
1607
+ } = config || {}; // Allow size to take on container bounds initially
1608
+
1609
+ if (!size) {
1610
+ var _canvas$parentElement, _canvas$parentElement2, _canvas$parentElement3, _canvas$parentElement4;
1611
+
1612
+ size = {
1613
+ width: (_canvas$parentElement = (_canvas$parentElement2 = canvas.parentElement) == null ? void 0 : _canvas$parentElement2.clientWidth) != null ? _canvas$parentElement : 0,
1614
+ height: (_canvas$parentElement3 = (_canvas$parentElement4 = canvas.parentElement) == null ? void 0 : _canvas$parentElement4.clientHeight) != null ? _canvas$parentElement3 : 0
1615
+ };
1616
+ }
1617
+
1618
+ let root = roots.get(canvas);
1619
+ let fiber = root == null ? void 0 : root.fiber;
1620
+ let store = root == null ? void 0 : root.store;
1621
+ let state = (_store = store) == null ? void 0 : _store.getState();
1622
+
1623
+ if (fiber && state) {
1624
+ // When a root was found, see if any fundamental props must be changed or exchanged
1625
+ // Check pixelratio
1626
+ if (props.dpr !== undefined && state.viewport.dpr !== calculateDpr(props.dpr)) state.setDpr(props.dpr); // Check size
1627
+
1628
+ if (state.size.width !== size.width || state.size.height !== size.height) state.setSize(size.width, size.height); // Check frameloop
1629
+
1630
+ if (state.frameloop !== props.frameloop) state.setFrameloop(props.frameloop); // For some props we want to reset the entire root
1631
+ // Changes to the color-space
1632
+
1633
+ const linearChanged = props.linear !== state.internal.lastProps.linear;
1634
+
1635
+ if (linearChanged) {
1636
+ unmountComponentAtNode(canvas);
1637
+ fiber = undefined;
1638
+ }
1639
+ }
1640
+
1641
+ if (!fiber) {
1642
+ // If no root has been found, make one
1643
+ // Create gl
1644
+ const glRenderer = createRendererInstance(gl, canvas); // Create store
1645
+
1646
+ store = createStore(applyProps, invalidate, advance, {
1647
+ gl: glRenderer,
1648
+ size,
1649
+ ...props
1650
+ });
1651
+ const state = store.getState(); // Create renderer
1652
+
1653
+ fiber = reconciler.createContainer(store, ConcurrentRoot, false, null); // Map it
1654
+
1655
+ roots.set(canvas, {
1656
+ fiber,
1657
+ store
1658
+ }); // Store events internally
1659
+
1660
+ if (events) state.set({
1661
+ events: events(store)
1662
+ });
1663
+ }
1664
+
1665
+ if (store && fiber) {
1666
+ reconciler.updateContainer( /*#__PURE__*/React.createElement(Provider, {
1667
+ store: store,
1668
+ element: element,
1669
+ onCreated: onCreated,
1670
+ target: canvas
1671
+ }), fiber, null, () => undefined);
1672
+ return store;
1673
+ } else {
1674
+ throw 'Error creating root!';
1675
+ }
1676
+ },
1677
+ unmount: () => unmountComponentAtNode(canvas)
1678
+ };
1679
+ }
1680
+
1681
+ function render(element, canvas, config = {}) {
1682
+ console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
1683
+ return createRoot(canvas, config).render(element);
1684
+ }
1685
+
1686
+ function Provider({
1687
+ store,
1688
+ element,
1689
+ onCreated,
1690
+ target
1691
+ }) {
1692
+ React.useEffect(() => {
1693
+ const state = store.getState(); // Flag the canvas active, rendering will now begin
1694
+
1695
+ state.set(state => ({
1696
+ internal: { ...state.internal,
1697
+ active: true
1698
+ }
1699
+ })); // Connect events
1700
+
1701
+ state.events.connect == null ? void 0 : state.events.connect(target); // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
1702
+
1703
+ if (onCreated) onCreated(state); // eslint-disable-next-line react-hooks/exhaustive-deps
1704
+ }, []);
1705
+ return /*#__PURE__*/React.createElement(context.Provider, {
1706
+ value: store
1707
+ }, element);
1708
+ }
1709
+
1710
+ function unmountComponentAtNode(canvas, callback) {
1711
+ const root = roots.get(canvas);
1712
+ const fiber = root == null ? void 0 : root.fiber;
1713
+
1714
+ if (fiber) {
1715
+ const state = root == null ? void 0 : root.store.getState();
1716
+ if (state) state.internal.active = false;
1717
+ reconciler.updateContainer(null, fiber, null, () => {
1718
+ if (state) {
1719
+ setTimeout(() => {
1720
+ try {
1721
+ var _state$gl, _state$gl$renderLists, _state$gl2, _state$gl3;
1722
+
1723
+ state.events.disconnect == null ? void 0 : state.events.disconnect();
1724
+ (_state$gl = state.gl) == null ? void 0 : (_state$gl$renderLists = _state$gl.renderLists) == null ? void 0 : _state$gl$renderLists.dispose == null ? void 0 : _state$gl$renderLists.dispose();
1725
+ (_state$gl2 = state.gl) == null ? void 0 : _state$gl2.forceContextLoss == null ? void 0 : _state$gl2.forceContextLoss();
1726
+ if ((_state$gl3 = state.gl) != null && _state$gl3.xr) state.internal.xr.disconnect();
1727
+ dispose(state);
1728
+ roots.delete(canvas);
1729
+ if (callback) callback(canvas);
1730
+ } catch (e) {
1731
+ /* ... */
1732
+ }
1733
+ }, 500);
1734
+ }
1735
+ });
1736
+ }
1737
+ }
1738
+
1739
+ const act = React.unstable_act;
1740
+
1741
+ function createPortal(children, container) {
1742
+ return reconciler.createPortal(children, container, null, null);
1743
+ }
1744
+
1745
+ reconciler.injectIntoDevTools({
1746
+ bundleType: process.env.NODE_ENV === 'production' ? 0 : 1,
1747
+ rendererPackageName: '@react-three/fiber',
1748
+ version: '18.0.0'
1749
+ });
1750
+
1751
+ export { createRoot as a, context as b, createEvents as c, createPortal as d, extend as e, reconciler as f, applyProps as g, dispose as h, invalidate as i, advance as j, addEffect as k, addAfterEffect as l, addTail as m, act as n, omit as o, pick as p, roots as q, render as r, useStore as s, threeTypes as t, unmountComponentAtNode as u, useThree as v, useFrame as w, useGraph as x, useLoader as y };