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

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,10 @@
1
- import * as React from 'react';
2
- import { suspend, preload, clear } from 'suspend-react';
3
1
  import * as THREE from 'three';
2
+ import * as React from 'react';
4
3
  import { DefaultEventPriority, ContinuousEventPriority, DiscreteEventPriority, ConcurrentRoot } from 'react-reconciler/constants';
5
4
  import create from 'zustand';
6
5
  import Reconciler from 'react-reconciler';
7
6
  import { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
7
+ import { suspend, preload, clear } from 'suspend-react';
8
8
 
9
9
  var threeTypes = /*#__PURE__*/Object.freeze({
10
10
  __proto__: null
@@ -66,7 +66,7 @@ const is = {
66
66
  const isArr = is.arr(a);
67
67
  if (isArr && arrays === 'reference') return a === b; // Array or Object, shallow compare first to see if it's a match
68
68
 
69
- if ((isArr || isObj) && a == b) return true; // Last resort, go through keys
69
+ if ((isArr || isObj) && a === b) return true; // Last resort, go through keys
70
70
 
71
71
  let i;
72
72
 
@@ -74,7 +74,13 @@ const is = {
74
74
 
75
75
  for (i in strict ? b : a) if (a[i] !== b[i]) return false;
76
76
 
77
- return is.und(i) ? a === b : true;
77
+ if (is.und(i)) {
78
+ if (isArr && a.length === 0 && b.length === 0) return true;
79
+ if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
80
+ if (a !== b) return false;
81
+ }
82
+
83
+ return true;
78
84
  }
79
85
 
80
86
  }; // Collects nodes and materials from a THREE.Object3D
@@ -109,7 +115,9 @@ function prepare(object, state) {
109
115
 
110
116
  if (state != null && state.primitive || !instance.__r3f) {
111
117
  instance.__r3f = {
118
+ type: '',
112
119
  root: null,
120
+ previousAttach: null,
113
121
  memoizedProps: {},
114
122
  eventCount: 0,
115
123
  handlers: {},
@@ -137,32 +145,42 @@ function resolve(instance, key) {
137
145
  target,
138
146
  key
139
147
  };
140
- }
148
+ } // Checks if a dash-cased string ends with an integer
141
149
 
150
+
151
+ const INDEX_REGEX = /-\d+$/;
142
152
  function attach(parent, child, type) {
143
153
  if (is.str(type)) {
154
+ // If attaching into an array (foo-0), create one
155
+ if (INDEX_REGEX.test(type)) {
156
+ const root = type.replace(INDEX_REGEX, '');
157
+ const {
158
+ target,
159
+ key
160
+ } = resolve(parent, root);
161
+ if (!Array.isArray(target[key])) target[key] = [];
162
+ }
163
+
144
164
  const {
145
165
  target,
146
166
  key
147
167
  } = resolve(parent, type);
148
- parent.__r3f.previousAttach = target[key];
168
+ child.__r3f.previousAttach = target[key];
149
169
  target[key] = child;
150
- } else if (is.arr(type)) {
151
- const [attach] = type;
152
- if (is.str(attach)) parent[attach](child);else if (is.fun(attach)) attach(parent, child);
153
- }
170
+ } else child.__r3f.previousAttach = type(parent, child);
154
171
  }
155
172
  function detach(parent, child, type) {
173
+ var _child$__r3f, _child$__r3f2;
174
+
156
175
  if (is.str(type)) {
157
176
  const {
158
177
  target,
159
178
  key
160
179
  } = resolve(parent, type);
161
- target[key] = parent.__r3f.previousAttach;
162
- } else if (is.arr(type)) {
163
- const [, detach] = type;
164
- if (is.str(detach)) parent[detach](child);else if (is.fun(detach)) detach(parent, child);
165
- }
180
+ target[key] = child.__r3f.previousAttach;
181
+ } else (_child$__r3f = child.__r3f) == null ? void 0 : _child$__r3f.previousAttach == null ? void 0 : _child$__r3f.previousAttach(parent, child);
182
+
183
+ (_child$__r3f2 = child.__r3f) == null ? true : delete _child$__r3f2.previousAttach;
166
184
  } // This function prepares a set of changes to be applied to the instance
167
185
 
168
186
  function diffProps(instance, {
@@ -250,11 +268,11 @@ function applyProps$1(instance, data) {
250
268
  if (value === DEFAULT + 'remove') {
251
269
  if (targetProp && targetProp.constructor) {
252
270
  // use the prop constructor to find the default it should be
253
- value = new targetProp.constructor(memoized.args);
271
+ value = new targetProp.constructor(...memoized.args);
254
272
  } else if (currentInstance.constructor) {
255
273
  // create a blank slate of the instance and copy the particular parameter.
256
274
  // @ts-ignore
257
- const defaultClassCall = new currentInstance.constructor(currentInstance.__r3f.memoizedProps.args);
275
+ const defaultClassCall = new currentInstance.constructor(...currentInstance.__r3f.memoizedProps.args);
258
276
  value = defaultClassCall[targetProp]; // destory the instance
259
277
 
260
278
  if (defaultClassCall.dispose) defaultClassCall.dispose(); // instance does not have constructor, just set it to 0
@@ -282,20 +300,10 @@ function applyProps$1(instance, data) {
282
300
 
283
301
  if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
284
302
  else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) targetProp.mask = value.mask; // Otherwise just set ...
285
- else targetProp.set(value); // Auto-convert sRGB colors, for now ...
286
- // https://github.com/pmndrs/react-three-fiber/issues/344
287
-
288
- if (!rootState.linear && isColor) targetProp.convertSRGBToLinear();
303
+ else targetProp.set(value);
289
304
  } // Else, just overwrite the value
290
305
 
291
- } else {
292
- currentInstance[key] = value; // Auto-convert sRGB textures, for now ...
293
- // https://github.com/pmndrs/react-three-fiber/issues/344
294
-
295
- if (!rootState.linear && currentInstance[key] instanceof THREE.Texture) {
296
- currentInstance[key].encoding = THREE.sRGBEncoding;
297
- }
298
- }
306
+ } else currentInstance[key] = value;
299
307
 
300
308
  invalidateInstance(instance);
301
309
  return instance;
@@ -746,23 +754,6 @@ function createEvents(store) {
746
754
  };
747
755
  }
748
756
 
749
- // Type guard to tell a store from a portal
750
- const isStore = def => def && !!def.getState;
751
-
752
- const getContainer = (container, child) => {
753
- var _container$__r3f$root, _container$__r3f;
754
-
755
- return {
756
- // If the container is not a root-store then it must be a THREE.Object3D into which part of the
757
- // scene is portalled into. Now there can be two variants of this, either that object is part of
758
- // the regular jsx tree, in which case it already has __r3f with a valid root attached, or it lies
759
- // outside react, in which case we must take the root of the child that is about to be attached to it.
760
- root: isStore(container) ? container : (_container$__r3f$root = (_container$__r3f = container.__r3f) == null ? void 0 : _container$__r3f.root) != null ? _container$__r3f$root : child.__r3f.root,
761
- // The container is the eventual target into which objects are mounted, it has to be a THREE.Object3D
762
- container: isStore(container) ? container.getState().scene : container
763
- };
764
- };
765
-
766
757
  let catalogue = {};
767
758
 
768
759
  let extend = objects => void (catalogue = { ...catalogue,
@@ -774,22 +765,9 @@ function createRenderer(roots, getEventPriority) {
774
765
  args = [],
775
766
  attach,
776
767
  ...props
777
- }, root, hostContext, internalInstanceHandle) {
768
+ }, root) {
778
769
  let name = `${type[0].toUpperCase()}${type.slice(1)}`;
779
- let instance; // https://github.com/facebook/react/issues/17147
780
- // Portals do not give us a root, they are themselves treated as a root by the reconciler
781
- // In order to figure out the actual root we have to climb through fiber internals :(
782
-
783
- if (!isStore(root) && internalInstanceHandle) {
784
- const fn = node => {
785
- if (!node.return) return node.stateNode && node.stateNode.containerInfo;else return fn(node.return);
786
- };
787
-
788
- root = fn(internalInstanceHandle);
789
- } // Assert that by now we have a valid root
790
-
791
-
792
- if (!root || !isStore(root)) throw `No valid root for ${name}!`; // Auto-attach geometries and materials
770
+ let instance; // Auto-attach geometries and materials
793
771
 
794
772
  if (attach === undefined) {
795
773
  if (name.endsWith('Geometry')) attach = 'geometry';else if (name.endsWith('Material')) attach = 'material';
@@ -799,6 +777,7 @@ function createRenderer(roots, getEventPriority) {
799
777
  if (props.object === undefined) throw `Primitives without 'object' are invalid!`;
800
778
  const object = props.object;
801
779
  instance = prepare(object, {
780
+ type,
802
781
  root,
803
782
  attach,
804
783
  primitive: true
@@ -815,19 +794,21 @@ function createRenderer(roots, getEventPriority) {
815
794
  // Append memoized props with args so it's not forgotten
816
795
 
817
796
  instance = prepare(new target(...args), {
797
+ type,
818
798
  root,
819
799
  attach,
820
800
  // TODO: Figure out what this is for
821
801
  memoizedProps: {
822
- args: args.length === 0 ? null : args
802
+ args
823
803
  }
824
804
  });
825
805
  } // It should NOT call onUpdate on object instanciation, because it hasn't been added to the
826
806
  // view yet. If the callback relies on references for instance, they won't be ready yet, this is
827
807
  // why it passes "true" here
808
+ // There is no reason to apply props to injects
828
809
 
829
810
 
830
- applyProps$1(instance, props);
811
+ if (name !== 'inject') applyProps$1(instance, props);
831
812
  return instance;
832
813
  }
833
814
 
@@ -949,11 +930,11 @@ function createRenderer(roots, getEventPriority) {
949
930
  }
950
931
 
951
932
  function switchInstance(instance, type, newProps, fiber) {
952
- var _instance$__r3f;
933
+ var _instance$__r3f, _instance$__r3f2;
953
934
 
954
935
  const parent = (_instance$__r3f = instance.__r3f) == null ? void 0 : _instance$__r3f.parent;
955
936
  if (!parent) return;
956
- const newInstance = createInstance(type, newProps, instance.__r3f.root); // https://github.com/pmndrs/react-three-fiber/issues/1348
937
+ const newInstance = createInstance(type, newProps, (_instance$__r3f2 = instance.__r3f) == null ? void 0 : _instance$__r3f2.root); // https://github.com/pmndrs/react-three-fiber/issues/1348
957
938
  // When args change the instance has to be re-constructed, which then
958
939
  // forces r3f to re-parent the children and non-scene objects
959
940
  // This can not include primitives, which should not have declarative children
@@ -986,19 +967,38 @@ function createRenderer(roots, getEventPriority) {
986
967
  }
987
968
 
988
969
  const reconciler = Reconciler({
989
- appendChildToContainer: (parentInstance, child) => {
990
- const {
991
- container,
992
- root
993
- } = getContainer(parentInstance, child); // Link current root to the default scene
970
+ createInstance,
971
+ removeChild,
972
+ appendChild,
973
+ appendInitialChild: appendChild,
974
+ insertBefore,
975
+ supportsMicrotask: true,
976
+ warnsIfNotActing: true,
977
+ supportsMutation: true,
978
+ isPrimaryRenderer: false,
979
+ noTimeout: -1,
980
+ appendChildToContainer: (container, child) => {
981
+ const scene = container.getState().scene; // Link current root to the default scene
994
982
 
995
- container.__r3f.root = root;
996
- appendChild(container, child);
983
+ scene.__r3f.root = container;
984
+ appendChild(scene, child);
985
+ },
986
+ removeChildFromContainer: (container, child) => removeChild(container.getState().scene, child),
987
+ insertInContainerBefore: (container, child, beforeChild) => insertBefore(container.getState().scene, child, beforeChild),
988
+ getRootHostContext: () => null,
989
+ getChildHostContext: parentHostContext => parentHostContext,
990
+
991
+ finalizeInitialChildren(instance) {
992
+ var _instance$__r3f3;
993
+
994
+ const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {}; // https://github.com/facebook/react/issues/20271
995
+ // Returning true will trigger commitMount
996
+
997
+ return !!localState.handlers;
997
998
  },
998
- removeChildFromContainer: (parentInstance, child) => removeChild(getContainer(parentInstance, child).container, child),
999
- insertInContainerBefore: (parentInstance, child, beforeChild) => insertBefore(getContainer(parentInstance, child).container, child, beforeChild),
1000
999
 
1001
1000
  prepareUpdate(instance, type, oldProps, newProps) {
1001
+ // Create diff-sets
1002
1002
  if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) {
1003
1003
  return [true];
1004
1004
  } else {
@@ -1031,40 +1031,58 @@ function createRenderer(roots, getEventPriority) {
1031
1031
  else applyProps$1(instance, diff);
1032
1032
  },
1033
1033
 
1034
+ commitMount(instance, type, props, int) {
1035
+ var _instance$__r3f4;
1036
+
1037
+ // https://github.com/facebook/react/issues/20271
1038
+ // This will make sure events are only added once to the central container
1039
+ const localState = (_instance$__r3f4 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f4 : {};
1040
+
1041
+ if (instance.raycast && localState.handlers && localState.eventCount) {
1042
+ instance.__r3f.root.getState().internal.interaction.push(instance);
1043
+ }
1044
+ },
1045
+
1046
+ getPublicInstance: instance => instance,
1047
+ shouldDeprioritizeSubtree: () => false,
1048
+ prepareForCommit: () => null,
1049
+ preparePortalMount: container => prepare(container.getState().scene),
1050
+ resetAfterCommit: () => {},
1051
+ shouldSetTextContent: () => false,
1052
+ clearContainer: () => false,
1053
+ detachDeletedInstance: () => {},
1054
+ createTextInstance: () => {},
1055
+
1034
1056
  hideInstance(instance) {
1035
- var _instance$__r3f2;
1057
+ var _instance$__r3f5;
1036
1058
 
1037
1059
  // Deatch while the instance is hidden
1038
1060
  const {
1039
1061
  attach: type,
1040
1062
  parent
1041
- } = (_instance$__r3f2 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f2 : {};
1063
+ } = (_instance$__r3f5 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f5 : {};
1042
1064
  if (type && parent) detach(parent, instance, type);
1043
1065
  if (instance.isObject3D) instance.visible = false;
1044
1066
  invalidateInstance(instance);
1045
1067
  },
1046
1068
 
1047
1069
  unhideInstance(instance, props) {
1048
- var _instance$__r3f3;
1070
+ var _instance$__r3f6;
1049
1071
 
1050
1072
  // Re-attach when the instance is unhidden
1051
1073
  const {
1052
1074
  attach: type,
1053
1075
  parent
1054
- } = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
1076
+ } = (_instance$__r3f6 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f6 : {};
1055
1077
  if (type && parent) attach(parent, instance, type);
1056
1078
  if (instance.isObject3D && props.visible == null || props.visible) instance.visible = true;
1057
1079
  invalidateInstance(instance);
1058
1080
  },
1059
1081
 
1060
- createInstance,
1061
- removeChild,
1062
- appendChild,
1063
- appendInitialChild: appendChild,
1064
- insertBefore,
1065
- warnsIfNotActing: true,
1066
- supportsMutation: true,
1067
- isPrimaryRenderer: false,
1082
+ hideTextInstance: () => {
1083
+ throw new Error('Text is not allowed in the R3F tree.');
1084
+ },
1085
+ // prettier-ignore
1068
1086
  getCurrentEventPriority: () => getEventPriority ? getEventPriority() : DefaultEventPriority,
1069
1087
  // @ts-ignore
1070
1088
  now: typeof performance !== 'undefined' && is.fun(performance.now) ? performance.now : is.fun(Date.now) ? Date.now : undefined,
@@ -1073,47 +1091,7 @@ function createRenderer(roots, getEventPriority) {
1073
1091
  // @ts-ignore
1074
1092
  cancelTimeout: is.fun(clearTimeout) ? clearTimeout : undefined,
1075
1093
  setTimeout: is.fun(setTimeout) ? setTimeout : undefined,
1076
- clearTimeout: is.fun(clearTimeout) ? clearTimeout : undefined,
1077
- noTimeout: -1,
1078
- hideTextInstance: () => {
1079
- throw new Error('Text is not allowed in the R3F tree.');
1080
- },
1081
- // prettier-ignore
1082
- getPublicInstance: instance => instance,
1083
- getRootHostContext: () => null,
1084
- getChildHostContext: parentHostContext => parentHostContext,
1085
- createTextInstance: () => {},
1086
-
1087
- finalizeInitialChildren(instance) {
1088
- var _instance$__r3f4;
1089
-
1090
- // https://github.com/facebook/react/issues/20271
1091
- // Returning true will trigger commitMount
1092
- const localState = (_instance$__r3f4 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f4 : {};
1093
- return !!localState.handlers;
1094
- },
1095
-
1096
- commitMount(instance)
1097
- /*, type, props*/
1098
- {
1099
- var _instance$__r3f5;
1100
-
1101
- // https://github.com/facebook/react/issues/20271
1102
- // This will make sure events are only added once to the central container
1103
- const localState = (_instance$__r3f5 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f5 : {};
1104
-
1105
- if (instance.raycast && localState.handlers && localState.eventCount) {
1106
- instance.__r3f.root.getState().internal.interaction.push(instance);
1107
- }
1108
- },
1109
-
1110
- shouldDeprioritizeSubtree: () => false,
1111
- prepareForCommit: () => null,
1112
- preparePortalMount: containerInfo => prepare(containerInfo),
1113
- resetAfterCommit: () => {},
1114
- shouldSetTextContent: () => false,
1115
- clearContainer: () => false,
1116
- detachDeletedInstance: () => {}
1094
+ clearTimeout: is.fun(clearTimeout) ? clearTimeout : undefined
1117
1095
  });
1118
1096
  return {
1119
1097
  reconciler,
@@ -1460,6 +1438,43 @@ function useStore() {
1460
1438
  function useThree(selector = state => state, equalityFn) {
1461
1439
  return useStore()(selector, equalityFn);
1462
1440
  }
1441
+ function useInject(state) {
1442
+ const useOriginalStore = useStore();
1443
+ const useInjectStore = React.useMemo(() => {
1444
+ const useInjected = (sel = state => state) => {
1445
+ // Execute the useStore hook with the selector once, to maintain reactivity, result doesn't matter
1446
+ useOriginalStore(sel); // Inject data and return the result, either selected or raw
1447
+
1448
+ return sel({ ...useOriginalStore.getState(),
1449
+ ...state
1450
+ });
1451
+ };
1452
+
1453
+ useInjected.setState = useOriginalStore.setState;
1454
+ useInjected.destroy = useOriginalStore.destroy; // Patch getState
1455
+
1456
+ useInjected.getState = () => {
1457
+ return { ...useOriginalStore.getState(),
1458
+ ...state
1459
+ };
1460
+ }; // Patch subscribe
1461
+
1462
+
1463
+ useInjected.subscribe = listener => {
1464
+ return useOriginalStore.subscribe((current, previous) => listener({ ...current,
1465
+ ...state
1466
+ }, previous));
1467
+ };
1468
+
1469
+ return useInjected;
1470
+ }, [useOriginalStore, state]); // Return the patched store and a provider component
1471
+
1472
+ return React.useMemo(() => [({
1473
+ children
1474
+ }) => /*#__PURE__*/React.createElement(context.Provider, {
1475
+ value: useInjectStore
1476
+ }, children), useInjectStore], [useInjectStore]);
1477
+ }
1463
1478
  function useFrame(callback, renderPriority = 0) {
1464
1479
  const subscribe = useStore().getState().internal.subscribe; // Update ref
1465
1480
 
@@ -1623,7 +1638,7 @@ function createRoot(canvas) {
1623
1638
 
1624
1639
  const handleSessionChange = () => {
1625
1640
  const gl = store.getState().gl;
1626
- gl.xr.enabled = gl.xr.isPresenting; // @ts-expect-error
1641
+ gl.xr.enabled = gl.xr.isPresenting; // @ts-ignore
1627
1642
  // WebXRManager's signature is incorrect.
1628
1643
  // See: https://github.com/pmndrs/react-three-fiber/pull/2017#discussion_r790134505
1629
1644
 
@@ -1665,6 +1680,7 @@ function createRoot(canvas) {
1665
1680
  } // Set color management
1666
1681
 
1667
1682
 
1683
+ if (THREE.ColorManagement) THREE.ColorManagement.legacyMode = false;
1668
1684
  const outputEncoding = linear ? THREE.LinearEncoding : THREE.sRGBEncoding;
1669
1685
  const toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;
1670
1686
  if (gl.outputEncoding !== outputEncoding) gl.outputEncoding = outputEncoding;
@@ -1780,10 +1796,29 @@ function unmountComponentAtNode(canvas, callback) {
1780
1796
  }
1781
1797
  }
1782
1798
 
1783
- const act = React.unstable_act;
1799
+ function createPortal(children, container, state) {
1800
+ return /*#__PURE__*/React.createElement(Portal, {
1801
+ children: children,
1802
+ container: container,
1803
+ state: state
1804
+ });
1805
+ }
1784
1806
 
1785
- function createPortal(children, container) {
1786
- return reconciler.createPortal(children, container, null, null);
1807
+ function Portal({
1808
+ state,
1809
+ children,
1810
+ container
1811
+ }) {
1812
+ /** This has to be a component because it would not be able to call useThree/useStore otherwise since
1813
+ * if this is our environment, then we are in in r3f's renderer but in react-dom, it would trigger
1814
+ * the "R3F hooks can only be used within the Canvas component!" warning:
1815
+ * <Canvas>
1816
+ * {createPortal(...)} */
1817
+ const portalState = React.useMemo(() => ({ ...state,
1818
+ scene: container
1819
+ }), [state, container]);
1820
+ const [PortalProvider, portalRoot] = useInject(portalState);
1821
+ return /*#__PURE__*/React.createElement(React.Fragment, null, reconciler.createPortal( /*#__PURE__*/React.createElement(PortalProvider, null, children), portalRoot, null));
1787
1822
  }
1788
1823
 
1789
1824
  reconciler.injectIntoDevTools({
@@ -1791,5 +1826,6 @@ reconciler.injectIntoDevTools({
1791
1826
  rendererPackageName: '@react-three/fiber',
1792
1827
  version: '18.0.0'
1793
1828
  });
1829
+ const act = React.unstable_act;
1794
1830
 
1795
- 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 };
1831
+ 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, useInject as w, useFrame as x, useGraph as y, useLoader as z };