@react-three/fiber 8.0.0-alpha-05 → 8.0.0-alpha-09

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,10 +1,9 @@
1
1
  import * as THREE from 'three';
2
2
  import * as React from 'react';
3
- import { DefaultEventPriority, ConcurrentRoot } from 'react-reconciler/constants';
3
+ import { DefaultEventPriority, IdleEventPriority, ContinuousEventPriority, DiscreteEventPriority, ConcurrentRoot } from 'react-reconciler/constants';
4
4
  import create from 'zustand';
5
5
  import shallow from 'zustand/shallow';
6
6
  import Reconciler from 'react-reconciler';
7
- import { unstable_now, unstable_runWithPriority, unstable_IdlePriority } from 'scheduler';
8
7
  import { useAsset } from 'use-asset';
9
8
  import mergeRefs from 'react-merge-refs';
10
9
  import useMeasure from 'react-use-measure';
@@ -13,6 +12,9 @@ var threeTypes = /*#__PURE__*/Object.freeze({
13
12
  __proto__: null
14
13
  });
15
14
 
15
+ const DEFAULT = '__default';
16
+ const isDiffSet = def => def && !!def.memoized && !!def.changes;
17
+ // A collection of compare functions
16
18
  const is = {
17
19
  obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
18
20
  fun: a => typeof a === 'function',
@@ -38,10 +40,181 @@ const is = {
38
40
  return is.und(i) ? a === b : true;
39
41
  }
40
42
 
41
- };
43
+ }; // Each object in the scene carries a small LocalState descriptor
44
+
45
+ function prepare(object, state) {
46
+ const instance = object;
47
+
48
+ if (state != null && state.primitive || !instance.__r3f) {
49
+ instance.__r3f = {
50
+ root: null,
51
+ memoizedProps: {},
52
+ handlers: {
53
+ count: 0
54
+ },
55
+ objects: [],
56
+ ...state
57
+ };
58
+ }
59
+
60
+ return object;
61
+ } // Shallow check arrays, but check objects atomically
62
+
63
+ function checkShallow(a, b) {
64
+ if (is.arr(a) && is.equ(a, b)) return true;
65
+ if (a === b) return true;
66
+ return false;
67
+ } // This function prepares a set of changes to be applied to the instance
68
+
69
+
70
+ function diffProps(instance, {
71
+ children: cN,
72
+ key: kN,
73
+ ref: rN,
74
+ ...props
75
+ }, {
76
+ children: cP,
77
+ key: kP,
78
+ ref: rP,
79
+ ...previous
80
+ } = {}, remove = false) {
81
+ var _instance$__r3f;
82
+
83
+ const localState = (_instance$__r3f = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f : {};
84
+ const entries = Object.entries(props);
85
+ const changes = []; // Catch removed props, prepend them so they can be reset or removed
86
+
87
+ if (remove) {
88
+ const previousKeys = Object.keys(previous);
89
+
90
+ for (let i = 0; i < previousKeys.length; i++) if (!props.hasOwnProperty(previousKeys[i])) entries.unshift([previousKeys[i], DEFAULT + 'remove']);
91
+ }
92
+
93
+ entries.forEach(([key, value]) => {
94
+ var _instance$__r3f2;
95
+
96
+ // Bail out on primitive object
97
+ if ((_instance$__r3f2 = instance.__r3f) != null && _instance$__r3f2.primitive && key === 'object') return; // When props match bail out
98
+
99
+ if (checkShallow(value, previous[key])) return;
100
+ let currentInstance = instance;
101
+ let targetProp = currentInstance[key]; // Collect handlers and bail out
102
+
103
+ if (/^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/.test(key)) return changes.push([key, value, true, currentInstance, targetProp]); // Revolve dashed props
104
+
105
+ if (key.includes('-')) {
106
+ const entries = key.split('-');
107
+ targetProp = entries.reduce((acc, key) => acc[key], instance); // If the target is atomic, it forces us to switch the root
108
+
109
+ if (!(targetProp && targetProp.set)) {
110
+ const [name, ...reverseEntries] = entries.reverse();
111
+ currentInstance = reverseEntries.reverse().reduce((acc, key) => acc[key], instance);
112
+ key = name;
113
+ }
114
+ }
115
+
116
+ changes.push([key, value, false, currentInstance, targetProp]);
117
+ });
118
+ const memoized = { ...props
119
+ };
120
+ if (localState.memoizedProps && localState.memoizedProps.args) memoized.args = localState.memoizedProps.args;
121
+ if (localState.memoizedProps && localState.memoizedProps.attach) memoized.attach = localState.memoizedProps.attach;
122
+ return {
123
+ memoized,
124
+ changes
125
+ };
126
+ } // This function applies a set of changes to the instance
127
+
128
+ function applyProps$1(instance, data) {
129
+ var _instance$__r3f3, _root$getState, _localState$handlers, _localState$handlers2;
130
+
131
+ // Filter equals, events and reserved props
132
+ const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
133
+ const root = localState.root;
134
+ const rootState = (_root$getState = root == null ? void 0 : root.getState == null ? void 0 : root.getState()) != null ? _root$getState : {};
135
+ const {
136
+ memoized,
137
+ changes
138
+ } = isDiffSet(data) ? data : diffProps(instance, data);
139
+ const prevHandlers = (_localState$handlers = localState.handlers) == null ? void 0 : _localState$handlers.count; // Prepare memoized props
140
+
141
+ if (instance.__r3f) instance.__r3f.memoizedProps = memoized;
142
+ changes.forEach(([key, value, isEvent, currentInstance, targetProp]) => {
143
+ // https://github.com/mrdoob/three.js/issues/21209
144
+ // HMR/fast-refresh relies on the ability to cancel out props, but threejs
145
+ // has no means to do this. Hence we curate a small collection of value-classes
146
+ // with their respective constructor/set arguments
147
+ // For removed props, try to set default values, if possible
148
+ if (value === DEFAULT + 'remove') {
149
+ if (targetProp && targetProp.constructor) {
150
+ // use the prop constructor to find the default it should be
151
+ value = new targetProp.constructor(memoized.args);
152
+ } else if (currentInstance.constructor) {
153
+ // create a blank slate of the instance and copy the particular parameter.
154
+ // @ts-ignore
155
+ const defaultClassCall = new currentInstance.constructor(currentInstance.__r3f.memoizedProps.args);
156
+ value = defaultClassCall[targetProp]; // destory the instance
157
+
158
+ if (defaultClassCall.dispose) defaultClassCall.dispose(); // instance does not have constructor, just set it to 0
159
+ } else value = 0;
160
+ } // Deal with pointer events ...
161
+
162
+
163
+ if (isEvent) {
164
+ if (value) localState.handlers[key] = value;else delete localState.handlers[key];
165
+ localState.handlers.count = Object.keys(localState.handlers).length;
166
+ } // Special treatment for objects with support for set/copy, and layers
167
+ else if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE.Layers)) {
168
+ // If value is an array
169
+ if (Array.isArray(value)) {
170
+ if (targetProp.fromArray) targetProp.fromArray(value);else targetProp.set(...value);
171
+ } // Test again target.copy(class) next ...
172
+ 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
173
+ // https://github.com/react-spring/react-three-fiber/issues/274
174
+ else if (value !== undefined) {
175
+ const isColor = targetProp instanceof THREE.Color; // Allow setting array scalars
176
+
177
+ if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
178
+ else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) targetProp.mask = value.mask; // Otherwise just set ...
179
+ else targetProp.set(value); // Auto-convert sRGB colors, for now ...
180
+ // https://github.com/react-spring/react-three-fiber/issues/344
181
+
182
+ if (!rootState.linear && isColor) targetProp.convertSRGBToLinear();
183
+ } // Else, just overwrite the value
184
+
185
+ } else {
186
+ currentInstance[key] = value; // Auto-convert sRGB textures, for now ...
187
+ // https://github.com/react-spring/react-three-fiber/issues/344
188
+
189
+ if (!rootState.linear && currentInstance[key] instanceof THREE.Texture) currentInstance[key].encoding = THREE.sRGBEncoding;
190
+ }
191
+
192
+ invalidateInstance(instance);
193
+ });
194
+
195
+ if (rootState.internal && instance.raycast && prevHandlers !== ((_localState$handlers2 = localState.handlers) == null ? void 0 : _localState$handlers2.count)) {
196
+ // Pre-emptively remove the instance from the interaction manager
197
+ const index = rootState.internal.interaction.indexOf(instance);
198
+ if (index > -1) rootState.internal.interaction.splice(index, 1); // Add the instance to the interaction manager only when it has handlers
199
+
200
+ if (localState.handlers.count) rootState.internal.interaction.push(instance);
201
+ } // Call the update lifecycle when it is being updated, but only when it is part of the scene
202
+
203
+
204
+ if (changes.length && instance.parent) updateInstance(instance);
205
+ }
206
+ function invalidateInstance(instance) {
207
+ var _instance$__r3f4, _instance$__r3f4$root;
208
+
209
+ const state = (_instance$__r3f4 = instance.__r3f) == null ? void 0 : (_instance$__r3f4$root = _instance$__r3f4.root) == null ? void 0 : _instance$__r3f4$root.getState == null ? void 0 : _instance$__r3f4$root.getState();
210
+ if (state && state.internal.frames === 0) state.invalidate();
211
+ }
212
+ function updateInstance(instance) {
213
+ instance.onUpdate == null ? void 0 : instance.onUpdate(instance);
214
+ }
42
215
 
43
216
  function makeId(event) {
44
- return (event.eventObject || event.object).uuid + '/' + event.index;
217
+ return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
45
218
  }
46
219
 
47
220
  function removeInteractivity(store, object) {
@@ -99,11 +272,7 @@ function createEvents(store) {
99
272
 
100
273
 
101
274
  function filterPointerEvents(objects) {
102
- return objects.filter(obj => ['Move', 'Over', 'Enter', 'Out', 'Leave'].some(name => {
103
- var _r3f$handlers;
104
-
105
- return (_r3f$handlers = obj.__r3f.handlers) == null ? void 0 : _r3f$handlers['onPointer' + name];
106
- }));
275
+ return objects.filter(obj => ['Move', 'Over', 'Enter', 'Out', 'Leave'].some(name => obj.__r3f.handlers['onPointer' + name]));
107
276
  }
108
277
 
109
278
  function intersect(filter) {
@@ -133,10 +302,7 @@ function createEvents(store) {
133
302
  let eventObject = intersect.object; // Bubble event up
134
303
 
135
304
  while (eventObject) {
136
- var _r3f;
137
-
138
- const handlers = (_r3f = eventObject.__r3f) == null ? void 0 : _r3f.handlers;
139
- if (handlers) intersections.push({ ...intersect,
305
+ if (eventObject.__r3f.handlers.count) intersections.push({ ...intersect,
140
306
  eventObject
141
307
  });
142
308
  eventObject = eventObject.parent;
@@ -273,12 +439,12 @@ function createEvents(store) {
273
439
  Array.from(internal.hovered.values()).forEach(hoveredObj => {
274
440
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
275
441
  // we call onPointerOut and delete the object from the hovered-elements map
276
- if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index)) {
442
+ if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
277
443
  const eventObject = hoveredObj.eventObject;
278
444
  const handlers = eventObject.__r3f.handlers;
279
445
  internal.hovered.delete(makeId(hoveredObj));
280
446
 
281
- if (handlers) {
447
+ if (handlers.count) {
282
448
  // Clear out intersects, they are outdated by now
283
449
  const data = { ...hoveredObj,
284
450
  intersections: hits || []
@@ -327,7 +493,7 @@ function createEvents(store) {
327
493
  const eventObject = data.eventObject;
328
494
  const handlers = eventObject.__r3f.handlers; // Check presence of handlers
329
495
 
330
- if (!handlers) return;
496
+ if (!handlers.count) return;
331
497
 
332
498
  if (isPointerMove) {
333
499
  // Move event ...
@@ -351,14 +517,14 @@ function createEvents(store) {
351
517
  handlers.onPointerMove == null ? void 0 : handlers.onPointerMove(data);
352
518
  } else {
353
519
  // All other events ...
354
- const handler = handlers == null ? void 0 : handlers[name];
520
+ const handler = handlers[name];
355
521
 
356
522
  if (handler) {
357
523
  // Forward all events back to their respective handlers with the exception of click events,
358
524
  // which must use the initial target
359
525
  if (name !== 'onClick' && name !== 'onContextMenu' && name !== 'onDoubleClick' || internal.initialHits.includes(eventObject)) {
360
526
  handler(data);
361
- pointerMissed(event, internal.interaction.filter(object => object !== eventObject));
527
+ pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
362
528
  }
363
529
  }
364
530
  }
@@ -381,9 +547,9 @@ function createEvents(store) {
381
547
 
382
548
  function pointerMissed(event, objects) {
383
549
  objects.forEach(object => {
384
- var _r3f$handlers2;
550
+ var _r3f$handlers$onPoin, _r3f$handlers;
385
551
 
386
- return (_r3f$handlers2 = object.__r3f.handlers) == null ? void 0 : _r3f$handlers2.onPointerMissed == null ? void 0 : _r3f$handlers2.onPointerMissed(event);
552
+ return (_r3f$handlers$onPoin = (_r3f$handlers = object.__r3f.handlers).onPointerMissed) == null ? void 0 : _r3f$handlers$onPoin.call(_r3f$handlers, event);
387
553
  });
388
554
  }
389
555
 
@@ -409,226 +575,13 @@ const getContainer = (container, child) => {
409
575
  };
410
576
  };
411
577
 
412
- const DEFAULT = '__default';
413
- const EMPTY = {};
414
- const FILTER = ['children', 'key', 'ref'];
415
578
  let catalogue = {};
416
579
 
417
580
  let extend = objects => void (catalogue = { ...catalogue,
418
581
  ...objects
419
- }); // Each object in the scene carries a small LocalState descriptor
420
-
421
-
422
- function prepare(object, state) {
423
- const instance = object;
424
-
425
- if (state != null && state.instance || !instance.__r3f) {
426
- instance.__r3f = {
427
- root: null,
428
- memoizedProps: {},
429
- objects: [],
430
- ...state
431
- };
432
- }
433
-
434
- return object;
435
- }
436
-
437
- function createRenderer(roots) {
438
- function applyProps(instance, newProps, oldProps = {}, accumulative = false) {
439
- var _instance$__r3f, _root$getState, _instance$__r3f2;
440
-
441
- // Filter equals, events and reserved props
442
- const localState = (_instance$__r3f = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f : {};
443
- const root = localState.root;
444
- const rootState = (_root$getState = root == null ? void 0 : root.getState == null ? void 0 : root.getState()) != null ? _root$getState : {};
445
- const sameProps = [];
446
- const handlers = [];
447
- const newMemoizedProps = {};
448
- let i = 0;
449
- Object.entries(newProps).forEach(([key, entry]) => {
450
- // we don't want children, ref or key in the memoized props
451
- if (FILTER.indexOf(key) === -1) {
452
- newMemoizedProps[key] = entry;
453
- }
454
- });
455
-
456
- if (localState.memoizedProps && localState.memoizedProps.args) {
457
- newMemoizedProps.args = localState.memoizedProps.args;
458
- }
459
-
460
- if (localState.memoizedProps && localState.memoizedProps.attach) {
461
- newMemoizedProps.attach = localState.memoizedProps.attach;
462
- }
463
-
464
- if (instance.__r3f) {
465
- instance.__r3f.memoizedProps = newMemoizedProps;
466
- }
467
-
468
- let objectKeys = Object.keys(newProps);
469
-
470
- for (i = 0; i < objectKeys.length; i++) {
471
- if (is.equ(newProps[objectKeys[i]], oldProps[objectKeys[i]])) {
472
- sameProps.push(objectKeys[i]);
473
- } // Event-handlers ...
474
- // are functions, that
475
- // start with "on", and
476
- // contain the name "Pointer", "Click", "DoubleClick", "ContextMenu", or "Wheel"
477
-
478
-
479
- if (is.fun(newProps[objectKeys[i]]) && /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/.test(objectKeys[i])) {
480
- handlers.push(objectKeys[i]);
481
- }
482
- } // Catch props that existed, but now exist no more ...
483
-
484
-
485
- const leftOvers = [];
486
-
487
- if (accumulative) {
488
- objectKeys = Object.keys(oldProps);
489
-
490
- for (i = 0; i < objectKeys.length; i++) {
491
- if (!newProps.hasOwnProperty(objectKeys[i])) {
492
- leftOvers.push(objectKeys[i]);
493
- }
494
- }
495
- }
496
-
497
- const toFilter = [...sameProps, ...FILTER]; // Instances use "object" as a reserved identifier
498
-
499
- if ((_instance$__r3f2 = instance.__r3f) != null && _instance$__r3f2.instance) toFilter.push('object');
500
- const filteredProps = { ...newProps
501
- }; // Removes sameProps and reserved props from newProps
502
-
503
- objectKeys = Object.keys(filteredProps);
504
-
505
- for (i = 0; i < objectKeys.length; i++) {
506
- if (toFilter.indexOf(objectKeys[i]) > -1) {
507
- delete filteredProps[objectKeys[i]];
508
- }
509
- } // Collect all new props
510
-
511
-
512
- const filteredPropsEntries = Object.entries(filteredProps); // Prepend left-overs so they can be reset or removed
513
- // Left-overs must come first!
514
-
515
- for (i = 0; i < leftOvers.length; i++) {
516
- if (leftOvers[i] !== 'children') {
517
- filteredPropsEntries.unshift([leftOvers[i], DEFAULT + 'remove']);
518
- }
519
- }
520
-
521
- if (filteredPropsEntries.length > 0) {
522
- filteredPropsEntries.forEach(([key, value]) => {
523
- if (!handlers.includes(key)) {
524
- let currentInstance = instance;
525
- let targetProp = currentInstance[key];
526
-
527
- if (key.includes('-')) {
528
- const entries = key.split('-');
529
- targetProp = entries.reduce((acc, key) => acc[key], instance); // If the target is atomic, it forces us to switch the root
530
-
531
- if (!(targetProp && targetProp.set)) {
532
- const [name, ...reverseEntries] = entries.reverse();
533
- currentInstance = reverseEntries.reverse().reduce((acc, key) => acc[key], instance);
534
- key = name;
535
- }
536
- } // https://github.com/mrdoob/three.js/issues/21209
537
- // HMR/fast-refresh relies on the ability to cancel out props, but threejs
538
- // has no means to do this. Hence we curate a small collection of value-classes
539
- // with their respective constructor/set arguments
540
- // For removed props, try to set default values, if possible
541
-
542
-
543
- if (value === DEFAULT + 'remove') {
544
- if (targetProp && targetProp.constructor) {
545
- // use the prop constructor to find the default it should be
546
- value = new targetProp.constructor(newMemoizedProps.args);
547
- } else if (currentInstance.constructor) {
548
- // create a blank slate of the instance and copy the particular parameter.
549
- // @ts-ignore
550
- const defaultClassCall = new currentInstance.constructor(currentInstance.__r3f.memoizedProps.args);
551
- value = defaultClassCall[targetProp]; // destory the instance
552
-
553
- if (defaultClassCall.dispose) {
554
- defaultClassCall.dispose();
555
- }
556
- } else {
557
- // instance does not have constructor, just set it to 0
558
- value = 0;
559
- }
560
- } // Special treatment for objects with support for set/copy, and layers
561
-
562
-
563
- if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE.Layers)) {
564
- // If value is an array
565
- if (Array.isArray(value)) {
566
- if (targetProp.fromArray) {
567
- targetProp.fromArray(value);
568
- } else {
569
- targetProp.set(...value);
570
- }
571
- } // Test again target.copy(class) next ...
572
- else if (targetProp.copy && value && value.constructor && targetProp.constructor.name === value.constructor.name) {
573
- targetProp.copy(value);
574
- } // If nothing else fits, just set the single value, ignore undefined
575
- // https://github.com/react-spring/react-three-fiber/issues/274
576
- else if (value !== undefined) {
577
- const isColor = targetProp instanceof THREE.Color; // Allow setting array scalars
578
-
579
- if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
580
- else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) targetProp.mask = value.mask; // Otherwise just set ...
581
- else targetProp.set(value); // Auto-convert sRGB colors, for now ...
582
- // https://github.com/react-spring/react-three-fiber/issues/344
583
-
584
- if (!rootState.linear && isColor) targetProp.convertSRGBToLinear();
585
- } // Else, just overwrite the value
586
-
587
- } else {
588
- currentInstance[key] = value; // Auto-convert sRGB textures, for now ...
589
- // https://github.com/react-spring/react-three-fiber/issues/344
590
-
591
- if (!rootState.linear && currentInstance[key] instanceof THREE.Texture) currentInstance[key].encoding = THREE.sRGBEncoding;
592
- }
593
-
594
- invalidateInstance(instance);
595
- }
596
- }); // Preemptively delete the instance from the containers interaction
597
-
598
- if (accumulative && root && instance.raycast && localState.handlers) {
599
- localState.handlers = undefined;
600
- const index = rootState.internal.interaction.indexOf(instance);
601
- if (index > -1) rootState.internal.interaction.splice(index, 1);
602
- } // Prep interaction handlers
603
-
604
-
605
- if (handlers.length) {
606
- if (accumulative && root && instance.raycast) {
607
- rootState.internal.interaction.push(instance);
608
- } // Add handlers to the instances handler-map
609
-
610
-
611
- localState.handlers = handlers.reduce((acc, key) => ({ ...acc,
612
- [key]: newProps[key]
613
- }), {});
614
- } // Call the update lifecycle when it is being updated, but only when it is part of the scene
615
-
616
-
617
- if (instance.parent) updateInstance(instance);
618
- }
619
- }
620
-
621
- function invalidateInstance(instance) {
622
- var _instance$__r3f3, _instance$__r3f3$root;
623
-
624
- const state = (_instance$__r3f3 = instance.__r3f) == null ? void 0 : (_instance$__r3f3$root = _instance$__r3f3.root) == null ? void 0 : _instance$__r3f3$root.getState == null ? void 0 : _instance$__r3f3$root.getState();
625
- if (state && state.internal.frames === 0) state.invalidate();
626
- }
627
-
628
- function updateInstance(instance) {
629
- instance.onUpdate == null ? void 0 : instance.onUpdate(instance);
630
- }
582
+ });
631
583
 
584
+ function createRenderer(roots, getEventPriority) {
632
585
  function createInstance(type, {
633
586
  args = [],
634
587
  ...props
@@ -654,7 +607,7 @@ function createRenderer(roots) {
654
607
  const object = props.object;
655
608
  instance = prepare(object, {
656
609
  root,
657
- instance: true
610
+ primitive: true
658
611
  });
659
612
  } else {
660
613
  const target = catalogue[name] || THREE[name];
@@ -688,7 +641,7 @@ function createRenderer(roots) {
688
641
  // why it passes "true" here
689
642
 
690
643
 
691
- applyProps(instance, props, {});
644
+ applyProps$1(instance, props);
692
645
  return instance;
693
646
  }
694
647
 
@@ -811,18 +764,18 @@ function createRenderer(roots) {
811
764
  // Never dispose of primitives because their state may be kept outside of React!
812
765
  // In order for an object to be able to dispose it has to have
813
766
  // - a dispose method,
814
- // - it cannot be an <instance object={...} />
767
+ // - it cannot be a <primitive object={...} />
815
768
  // - it cannot be a THREE.Scene, because three has broken it's own api
816
769
  //
817
770
  // Since disposal is recursive, we can check the optional dispose arg, which will be undefined
818
771
  // when the reconciler calls it, but then carry our own check recursively
819
772
 
820
773
 
821
- const isInstance = (_child$__r3f2 = child.__r3f) == null ? void 0 : _child$__r3f2.instance;
822
- const shouldDispose = dispose === undefined ? child.dispose !== null && !isInstance : dispose; // Remove nested child objects. Primitives should not have objects and children that are
774
+ const isPrimitive = (_child$__r3f2 = child.__r3f) == null ? void 0 : _child$__r3f2.primitive;
775
+ const shouldDispose = dispose === undefined ? child.dispose !== null && !isPrimitive : dispose; // Remove nested child objects. Primitives should not have objects and children that are
823
776
  // attached to them declaratively ...
824
777
 
825
- if (!isInstance) {
778
+ if (!isPrimitive) {
826
779
  var _child$__r3f3;
827
780
 
828
781
  removeRecursive((_child$__r3f3 = child.__r3f) == null ? void 0 : _child$__r3f3.objects, child, shouldDispose);
@@ -835,12 +788,18 @@ function createRenderer(roots) {
835
788
  delete child.__r3f.objects;
836
789
  delete child.__r3f.handlers;
837
790
  delete child.__r3f.memoizedProps;
838
- if (!isInstance) delete child.__r3f;
791
+ if (!isPrimitive) delete child.__r3f;
839
792
  } // Dispose item whenever the reconciler feels like it
840
793
 
841
794
 
842
795
  if (shouldDispose && child.dispose && child.type !== 'Scene') {
843
- unstable_runWithPriority(unstable_IdlePriority, () => child.dispose());
796
+ reconciler.runWithPriority(IdleEventPriority, () => {
797
+ try {
798
+ child.dispose();
799
+ } catch (e) {
800
+ /* ... */
801
+ }
802
+ });
844
803
  }
845
804
 
846
805
  invalidateInstance(parentInstance);
@@ -879,25 +838,6 @@ function createRenderer(roots) {
879
838
  }
880
839
 
881
840
  const reconciler = Reconciler({
882
- now: unstable_now,
883
- createInstance,
884
- removeChild,
885
- appendChild,
886
- appendInitialChild: appendChild,
887
- insertBefore,
888
- warnsIfNotActing: true,
889
- supportsMutation: true,
890
- isPrimaryRenderer: false,
891
- getCurrentEventPriority: () => DefaultEventPriority,
892
- // @ts-ignore
893
- scheduleTimeout: is.fun(setTimeout) ? setTimeout : undefined,
894
- // @ts-ignore
895
- cancelTimeout: is.fun(clearTimeout) ? clearTimeout : undefined,
896
- // @ts-ignore
897
- setTimeout: is.fun(setTimeout) ? setTimeout : undefined,
898
- // @ts-ignore
899
- clearTimeout: is.fun(clearTimeout) ? clearTimeout : undefined,
900
- noTimeout: -1,
901
841
  appendChildToContainer: (parentInstance, child) => {
902
842
  const {
903
843
  container,
@@ -907,46 +847,38 @@ function createRenderer(roots) {
907
847
  container.__r3f.root = root;
908
848
  appendChild(container, child);
909
849
  },
910
- removeChildFromContainer: (parentInstance, child) => {
911
- const {
912
- container
913
- } = getContainer(parentInstance, child);
914
- removeChild(container, child);
915
- },
916
- insertInContainerBefore: (parentInstance, child, beforeChild) => {
917
- const {
918
- container
919
- } = getContainer(parentInstance, child);
920
- insertBefore(container, child, beforeChild);
921
- },
850
+ removeChildFromContainer: (parentInstance, child) => removeChild(getContainer(parentInstance, child).container, child),
851
+ insertInContainerBefore: (parentInstance, child, beforeChild) => insertBefore(getContainer(parentInstance, child).container, child, beforeChild),
922
852
 
923
- commitUpdate(instance, updatePayload, type, oldProps, newProps, fiber) {
924
- if (instance.__r3f.instance && newProps.object && newProps.object !== instance) {
925
- // <instance object={...} /> where the object reference has changed
926
- switchInstance(instance, type, newProps, fiber);
927
- } else {
853
+ prepareUpdate(instance, type, oldProps, newProps) {
854
+ if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) return [true];else {
928
855
  // This is a data object, let's extract critical information about it
929
856
  const {
930
857
  args: argsNew = [],
858
+ children: cN,
931
859
  ...restNew
932
860
  } = newProps;
933
861
  const {
934
862
  args: argsOld = [],
863
+ children: cO,
935
864
  ...restOld
936
865
  } = oldProps; // If it has new props or arguments, then it needs to be re-instanciated
937
866
 
938
- const hasNewArgs = argsNew.some((value, index) => is.obj(value) ? Object.entries(value).some(([key, val]) => val !== argsOld[index][key]) : value !== argsOld[index]);
867
+ if (argsNew.some((value, index) => value !== argsOld[index])) return [true]; // Create a diff-set, flag if there are any changes
939
868
 
940
- if (hasNewArgs) {
941
- // Next we create a new instance and append it again
942
- switchInstance(instance, type, newProps, fiber);
943
- } else {
944
- // Otherwise just overwrite props
945
- applyProps(instance, restNew, restOld, true);
946
- }
869
+ const diff = diffProps(instance, restNew, restOld, true);
870
+ if (diff.changes.length) return [false, diff]; // Otherwise do not touch the instance
871
+
872
+ return null;
947
873
  }
948
874
  },
949
875
 
876
+ commitUpdate(instance, [reconstruct, diff], type, oldProps, newProps, fiber) {
877
+ // Reconstruct when args or <primitive object={...} have changes
878
+ if (reconstruct) switchInstance(instance, type, newProps, fiber); // Otherwise just overwrite props
879
+ else applyProps$1(instance, diff);
880
+ },
881
+
950
882
  hideInstance(instance) {
951
883
  if (instance.isObject3D) {
952
884
  instance.visible = false;
@@ -961,72 +893,45 @@ function createRenderer(roots) {
961
893
  }
962
894
  },
963
895
 
964
- hideTextInstance() {
896
+ createInstance,
897
+ removeChild,
898
+ appendChild,
899
+ appendInitialChild: appendChild,
900
+ insertBefore,
901
+ warnsIfNotActing: true,
902
+ supportsMutation: true,
903
+ isPrimaryRenderer: false,
904
+ getCurrentEventPriority: () => getEventPriority ? getEventPriority() : DefaultEventPriority,
905
+ // @ts-ignore
906
+ now: is.fun(performance.now) ? performance.now : is.fun(Date.now) ? Date.now : undefined,
907
+ // @ts-ignore
908
+ scheduleTimeout: is.fun(setTimeout) ? setTimeout : undefined,
909
+ // @ts-ignore
910
+ cancelTimeout: is.fun(clearTimeout) ? clearTimeout : undefined,
911
+ setTimeout: is.fun(setTimeout) ? setTimeout : undefined,
912
+ clearTimeout: is.fun(clearTimeout) ? clearTimeout : undefined,
913
+ noTimeout: -1,
914
+ hideTextInstance: () => {
965
915
  throw new Error('Text is not allowed in the R3F tree.');
966
916
  },
967
-
968
- getPublicInstance(instance) {
969
- // TODO: might fix switchInstance (?)
970
- return instance;
971
- },
972
-
973
- getRootHostContext(rootContainer) {
974
- return EMPTY;
975
- },
976
-
977
- getChildHostContext(parentHostContext) {
978
- return EMPTY;
979
- },
980
-
981
- createTextInstance() {},
982
-
983
- finalizeInitialChildren(instance) {
984
- // https://github.com/facebook/react/issues/20271
985
- // Returning true will trigger commitMount
986
- return !!instance.__r3f.handlers;
987
- },
988
-
989
- commitMount(instance)
990
- /*, type, props*/
991
- {
992
- // https://github.com/facebook/react/issues/20271
993
- // This will make sure events are only added once to the central container
994
- if (instance.raycast && instance.__r3f.handlers) instance.__r3f.root.getState().internal.interaction.push(instance);
995
- },
996
-
997
- prepareUpdate() {
998
- return EMPTY;
999
- },
1000
-
1001
- shouldDeprioritizeSubtree() {
1002
- return false;
1003
- },
1004
-
1005
- prepareForCommit() {
1006
- return null;
1007
- },
1008
-
1009
- preparePortalMount(...args) {// noop
1010
- },
1011
-
1012
- resetAfterCommit() {// noop
1013
- },
1014
-
1015
- shouldSetTextContent() {
1016
- return false;
1017
- },
1018
-
1019
- clearContainer() {
1020
- return false;
1021
- },
1022
-
1023
- detachDeletedInstance() {// noop
1024
- }
1025
-
917
+ // prettier-ignore
918
+ getPublicInstance: instance => instance,
919
+ getRootHostContext: () => null,
920
+ getChildHostContext: parentHostContext => parentHostContext,
921
+ createTextInstance: () => {},
922
+ finalizeInitialChildren: () => false,
923
+ commitMount: () => {},
924
+ shouldDeprioritizeSubtree: () => false,
925
+ prepareForCommit: () => null,
926
+ preparePortalMount: containerInfo => prepare(containerInfo),
927
+ resetAfterCommit: () => {},
928
+ shouldSetTextContent: () => false,
929
+ clearContainer: () => false,
930
+ detachDeletedInstance: () => {}
1026
931
  });
1027
932
  return {
1028
933
  reconciler,
1029
- applyProps
934
+ applyProps: applyProps$1
1030
935
  };
1031
936
  }
1032
937
 
@@ -1079,14 +984,14 @@ const createStore = (applyProps, invalidate, advance, props) => {
1079
984
  params: { ...raycaster.params,
1080
985
  ...params
1081
986
  }
1082
- }, {}); // Create default camera
987
+ }); // Create default camera
1083
988
 
1084
989
  const isCamera = cameraOptions instanceof THREE.Camera;
1085
990
  const camera = isCamera ? cameraOptions : orthographic ? new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE.PerspectiveCamera(75, 0, 0.1, 1000);
1086
991
 
1087
992
  if (!isCamera) {
1088
993
  camera.position.z = 5;
1089
- if (cameraOptions) applyProps(camera, cameraOptions, {}); // Always look at center by default
994
+ if (cameraOptions) applyProps(camera, cameraOptions); // Always look at center by default
1090
995
 
1091
996
  camera.lookAt(0, 0, 0);
1092
997
  }
@@ -1224,8 +1129,8 @@ const createStore = (applyProps, invalidate, advance, props) => {
1224
1129
  internal: { ...internal,
1225
1130
  // If this subscription was given a priority, it takes rendering into its own hands
1226
1131
  // For that reason we switch off automatic rendering and increase the manual flag
1227
- // As long as this flag is positive (there could be multiple render subscription)
1228
- // ..there can be no internal rendering at all
1132
+ // As long as this flag is positive there can be no internal rendering at all
1133
+ // because there could be multiple render subscriptions
1229
1134
  priority: internal.priority + (priority > 0 ? 1 : 0),
1230
1135
  // Register subscriber and sort layers from lowest to highest, meaning,
1231
1136
  // highest priority renders last (on top of the other frames)
@@ -1377,20 +1282,61 @@ function createLoop(roots) {
1377
1282
  };
1378
1283
  }
1379
1284
 
1285
+ // @ts-ignore
1286
+ const CLICK = 'click';
1287
+ const CONTEXTMENU = 'contextmenu';
1288
+ const DBLCLICK = 'dblclick';
1289
+ const POINTERCANCEL = 'pointercancel';
1290
+ const POINTERDOWN = 'pointerdown';
1291
+ const POINTERUP = 'pointerup';
1292
+ const POINTERMOVE = 'pointermove';
1293
+ const POINTEROUT = 'pointerout';
1294
+ const POINTEROVER = 'pointerover';
1295
+ const POINTERENTER = 'pointerenter';
1296
+ const POINTERLEAVE = 'pointerleave';
1297
+ const WHEEL = 'wheel'; // https://github.com/facebook/react/tree/main/packages/react-reconciler#getcurrenteventpriority
1298
+ // Gives React a clue as to how import the current interaction is
1299
+
1300
+ function getEventPriority() {
1301
+ var _window, _window$event;
1302
+
1303
+ let name = (_window = window) == null ? void 0 : (_window$event = _window.event) == null ? void 0 : _window$event.type;
1304
+
1305
+ switch (name) {
1306
+ case CLICK:
1307
+ case CONTEXTMENU:
1308
+ case DBLCLICK:
1309
+ case POINTERCANCEL:
1310
+ case POINTERDOWN:
1311
+ case POINTERUP:
1312
+ return DiscreteEventPriority;
1313
+
1314
+ case POINTERMOVE:
1315
+ case POINTEROUT:
1316
+ case POINTEROVER:
1317
+ case POINTERENTER:
1318
+ case POINTERLEAVE:
1319
+ case WHEEL:
1320
+ return ContinuousEventPriority;
1321
+
1322
+ default:
1323
+ return DefaultEventPriority;
1324
+ }
1325
+ }
1380
1326
  function createPointerEvents(store) {
1381
1327
  const {
1382
1328
  handlePointer
1383
1329
  } = createEvents(store);
1384
1330
  const names = {
1385
- onClick: ['click', false],
1386
- onContextMenu: ['contextmenu', false],
1387
- onDoubleClick: ['dblclick', false],
1388
- onWheel: ['wheel', true],
1389
- onPointerDown: ['pointerdown', true],
1390
- onPointerUp: ['pointerup', true],
1391
- onPointerLeave: ['pointerleave', true],
1392
- onPointerMove: ['pointermove', true],
1393
- onPointerCancel: ['pointercancel', true],
1331
+ onClick: [CLICK, false],
1332
+ onContextMenu: [CONTEXTMENU, false],
1333
+ onDoubleClick: [DBLCLICK, false],
1334
+ onWheel: [WHEEL, true],
1335
+ onPointerDown: [POINTERDOWN, true],
1336
+ onPointerUp: [POINTERUP, true],
1337
+ onPointerLeave: [POINTERLEAVE, true],
1338
+ onPointerMove: [POINTERMOVE, true],
1339
+ onPointerCancel: [POINTERCANCEL, true],
1394
1340
  onLostPointerCapture: ['lostpointercapture', true]
1395
1341
  };
1396
1342
  return {
@@ -1554,17 +1500,12 @@ function useThree(selector = state => state, equalityFn) {
1554
1500
  return useStore()(selector, equalityFn);
1555
1501
  }
1556
1502
  function useFrame(callback, renderPriority = 0) {
1557
- const {
1558
- subscribe
1559
- } = useStore().getState().internal; // Update ref
1503
+ const subscribe = useStore().getState().internal.subscribe; // Update ref
1560
1504
 
1561
1505
  const ref = React.useRef(callback);
1562
- React.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe/unsub
1506
+ React.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe on mount, unsubscribe on unmount
1563
1507
 
1564
- React.useLayoutEffect(() => {
1565
- const unsubscribe = subscribe(ref, renderPriority);
1566
- return () => unsubscribe();
1567
- }, [renderPriority, subscribe]);
1508
+ React.useLayoutEffect(() => subscribe(ref, renderPriority), [renderPriority, subscribe]);
1568
1509
  return null;
1569
1510
  }
1570
1511
 
@@ -1632,7 +1573,7 @@ const {
1632
1573
  const {
1633
1574
  reconciler,
1634
1575
  applyProps
1635
- } = createRenderer();
1576
+ } = createRenderer(roots, getEventPriority);
1636
1577
 
1637
1578
  const createRendererInstance = (gl, canvas) => isRenderer(gl) ? gl : new THREE.WebGLRenderer({
1638
1579
  powerPreference: 'high-performance',
@@ -1785,17 +1726,9 @@ function dispose(obj) {
1785
1726
  }
1786
1727
 
1787
1728
  const act = reconciler.act;
1788
- const hasSymbol = is.fun(Symbol) && Symbol.for;
1789
- const REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
1790
1729
 
1791
- function createPortal(children, container, implementation, key = null) {
1792
- return {
1793
- $$typeof: REACT_PORTAL_TYPE,
1794
- key: key == null ? null : '' + key,
1795
- children,
1796
- containerInfo: prepare(container),
1797
- implementation
1798
- };
1730
+ function createPortal(children, container) {
1731
+ return reconciler.createPortal(children, container, null, null);
1799
1732
  }
1800
1733
 
1801
1734
  reconciler.injectIntoDevTools({