@react-three/fiber 9.8.0 → 9.8.2
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.
- package/CHANGELOG.md +36 -0
- package/dist/declarations/src/core/configuration.d.ts +91 -0
- package/dist/declarations/src/core/extend.d.ts +12 -0
- package/dist/declarations/src/core/hooks.d.ts +2 -1
- package/dist/declarations/src/core/index.d.ts +10 -5
- package/dist/declarations/src/core/portal.d.ts +13 -0
- package/dist/declarations/src/core/reconciler.d.ts +11 -17
- package/dist/declarations/src/core/renderer.d.ts +8 -90
- package/dist/declarations/src/core/root.d.ts +37 -0
- package/dist/declarations/src/core/store.d.ts +1 -1
- package/dist/declarations/src/core/{promise.d.ts → utils/promise.d.ts} +7 -3
- package/dist/declarations/src/core/utils/react.d.ts +184 -0
- package/dist/declarations/src/core/utils.d.ts +9 -184
- package/dist/declarations/src/three-types.d.ts +1 -1
- package/dist/{events-ce5e34e8.cjs.dev.js → events-0dab3c53.cjs.dev.js} +800 -520
- package/dist/{events-a79ef12b.cjs.prod.js → events-98e95d43.cjs.prod.js} +801 -521
- package/dist/{events-803f4abc.esm.js → events-e51d7528.esm.js} +801 -521
- package/dist/react-three-fiber.cjs.dev.js +21 -4
- package/dist/react-three-fiber.cjs.prod.js +21 -4
- package/dist/react-three-fiber.esm.js +22 -5
- package/native/dist/react-three-fiber-native.cjs.dev.js +4 -3
- package/native/dist/react-three-fiber-native.cjs.prod.js +4 -3
- package/native/dist/react-three-fiber-native.esm.js +5 -4
- package/package.json +8 -2
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import React__default from 'react';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
4
5
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
5
6
|
import { suspend, preload, clear } from 'suspend-react';
|
|
7
|
+
import { useFiber, useContextBridge, traverseFiber, useActivityBridge as useActivityBridge$1 } from 'its-fine';
|
|
6
8
|
import w1, { unstable_getCurrentPriorityLevel, unstable_IdlePriority, unstable_LowPriority, unstable_NormalPriority, unstable_UserBlockingPriority, unstable_ImmediatePriority, unstable_scheduleCallback } from 'scheduler';
|
|
7
|
-
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
8
|
-
import { useFiber, useContextBridge, traverseFiber } from 'its-fine';
|
|
9
9
|
|
|
10
10
|
var threeTypes = /*#__PURE__*/Object.freeze({
|
|
11
11
|
__proto__: null
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* Returns the instance's initial (outmost) root.
|
|
16
|
-
*/
|
|
17
|
-
function findInitialRoot(instance) {
|
|
18
|
-
let root = instance.root;
|
|
19
|
-
while (root.getState().previousRoot) root = root.getState().previousRoot;
|
|
20
|
-
return root;
|
|
21
|
-
}
|
|
22
14
|
/**
|
|
23
15
|
* Safely flush async effects when testing, simulating a legacy root.
|
|
24
16
|
* @deprecated Import from React instead. import { act } from 'react'
|
|
@@ -26,9 +18,6 @@ function findInitialRoot(instance) {
|
|
|
26
18
|
// Reference with computed key to break Webpack static analysis
|
|
27
19
|
// https://github.com/webpack/webpack/issues/14814
|
|
28
20
|
const act = React['act' + ''];
|
|
29
|
-
const isOrthographicCamera = def => def && def.isOrthographicCamera;
|
|
30
|
-
const isRef = obj => obj && obj.hasOwnProperty('current');
|
|
31
|
-
const isColorRepresentation = value => value != null && (typeof value === 'string' || typeof value === 'number' || value.isColor);
|
|
32
21
|
|
|
33
22
|
/**
|
|
34
23
|
* An SSR-friendly useLayoutEffect.
|
|
@@ -45,7 +34,6 @@ function useMutableCallback(fn) {
|
|
|
45
34
|
useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
|
|
46
35
|
return ref;
|
|
47
36
|
}
|
|
48
|
-
const noop = () => {};
|
|
49
37
|
function Gate({
|
|
50
38
|
promise,
|
|
51
39
|
onSettled
|
|
@@ -60,7 +48,7 @@ function useGate() {
|
|
|
60
48
|
const [promise, setPromise] = React.useState(null);
|
|
61
49
|
const onSettled = React.useCallback(() => setPromise(null), []);
|
|
62
50
|
// Keep the first promise, or a repeated call would loop
|
|
63
|
-
const waitFor = React.useCallback(next => setPromise(current => current != null ? current : Promise.resolve(next).then(
|
|
51
|
+
const waitFor = React.useCallback(next => setPromise(current => current != null ? current : Promise.resolve(next).then(() => {}, () => {})), []);
|
|
64
52
|
const gate = promise ? /*#__PURE__*/jsx(React.Suspense, {
|
|
65
53
|
fallback: null,
|
|
66
54
|
children: /*#__PURE__*/jsx(Gate, {
|
|
@@ -70,23 +58,29 @@ function useGate() {
|
|
|
70
58
|
}) : null;
|
|
71
59
|
return [gate, waitFor];
|
|
72
60
|
}
|
|
61
|
+
|
|
62
|
+
// Activity is optional on React 19.0/19.1. A computed key also supports strict ESM bundlers.
|
|
63
|
+
const useActivityBridge = React['Activity' + ''] ? useActivityBridge$1 : () => React.Fragment;
|
|
73
64
|
/**
|
|
74
|
-
* Bridges
|
|
65
|
+
* Bridges Context, StrictMode, and Activity visibility from a primary renderer.
|
|
75
66
|
*/
|
|
76
67
|
function useBridge() {
|
|
77
68
|
const fiber = useFiber();
|
|
78
69
|
const ContextBridge = useContextBridge();
|
|
70
|
+
const ActivityBridge = useActivityBridge();
|
|
79
71
|
return React.useMemo(() => ({
|
|
80
72
|
children
|
|
81
73
|
}) => {
|
|
82
74
|
const strict = !!traverseFiber(fiber, true, node => node.type === React.StrictMode);
|
|
83
75
|
const Root = strict ? React.StrictMode : React.Fragment;
|
|
84
76
|
return /*#__PURE__*/jsx(Root, {
|
|
85
|
-
children: /*#__PURE__*/jsx(
|
|
86
|
-
children:
|
|
77
|
+
children: /*#__PURE__*/jsx(ActivityBridge, {
|
|
78
|
+
children: /*#__PURE__*/jsx(ContextBridge, {
|
|
79
|
+
children: children
|
|
80
|
+
})
|
|
87
81
|
})
|
|
88
82
|
});
|
|
89
|
-
}, [fiber, ContextBridge]);
|
|
83
|
+
}, [fiber, ContextBridge, ActivityBridge]);
|
|
90
84
|
}
|
|
91
85
|
function Block({
|
|
92
86
|
set
|
|
@@ -115,6 +109,19 @@ const ErrorBoundary = /* @__PURE__ */(_ErrorBoundary => (_ErrorBoundary = class
|
|
|
115
109
|
}, _ErrorBoundary.getDerivedStateFromError = () => ({
|
|
116
110
|
error: true
|
|
117
111
|
}), _ErrorBoundary))();
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Returns the instance's initial (outmost) root.
|
|
115
|
+
*/
|
|
116
|
+
function findInitialRoot(instance) {
|
|
117
|
+
let root = instance.root;
|
|
118
|
+
while (root.getState().previousRoot) root = root.getState().previousRoot;
|
|
119
|
+
return root;
|
|
120
|
+
}
|
|
121
|
+
const isOrthographicCamera = def => def && def.isOrthographicCamera;
|
|
122
|
+
const isRef = obj => obj && obj.hasOwnProperty('current');
|
|
123
|
+
const isColorRepresentation = value => value != null && (typeof value === 'string' || typeof value === 'number' || value.isColor);
|
|
124
|
+
const noop = () => {};
|
|
118
125
|
function calculateDpr(dpr) {
|
|
119
126
|
var _window$devicePixelRa;
|
|
120
127
|
// Err on the side of progress by assuming 2x dpr if we can't detect it
|
|
@@ -123,6 +130,43 @@ function calculateDpr(dpr) {
|
|
|
123
130
|
return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
|
|
124
131
|
}
|
|
125
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Calls `onChange` whenever window.devicePixelRatio changes: moving to another display or zooming.
|
|
135
|
+
* Neither resizes the canvas, so resize observers won't see it. A resolution query only matches the
|
|
136
|
+
* current ratio, so it is re-created after every change. Returns the unsubscribe function.
|
|
137
|
+
*/
|
|
138
|
+
function watchDpr(onChange) {
|
|
139
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function' || !window.devicePixelRatio) {
|
|
140
|
+
return () => {};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Partial matchMedia polyfills (test setups, older environments) may return no listener methods,
|
|
144
|
+
// or throw on a query they can't parse. Following the ratio is best-effort: it runs inside
|
|
145
|
+
// configuration, which must not fail because of it
|
|
146
|
+
let query;
|
|
147
|
+
const listen = () => {
|
|
148
|
+
try {
|
|
149
|
+
var _query, _query2;
|
|
150
|
+
query = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
|
|
151
|
+
if (typeof ((_query = query) == null ? void 0 : _query.addEventListener) === 'function') query.addEventListener('change', handleChange);else (_query2 = query) == null ? void 0 : _query2.addListener == null ? void 0 : _query2.addListener(handleChange);
|
|
152
|
+
} catch {
|
|
153
|
+
query = undefined;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const unlisten = () => {
|
|
157
|
+
var _query3, _query4;
|
|
158
|
+
if (typeof ((_query3 = query) == null ? void 0 : _query3.removeEventListener) === 'function') query.removeEventListener('change', handleChange);else (_query4 = query) == null ? void 0 : _query4.removeListener == null ? void 0 : _query4.removeListener(handleChange);
|
|
159
|
+
query = undefined;
|
|
160
|
+
};
|
|
161
|
+
const handleChange = () => {
|
|
162
|
+
unlisten();
|
|
163
|
+
listen();
|
|
164
|
+
onChange();
|
|
165
|
+
};
|
|
166
|
+
listen();
|
|
167
|
+
return unlisten;
|
|
168
|
+
}
|
|
169
|
+
|
|
126
170
|
/**
|
|
127
171
|
* Returns instance root state
|
|
128
172
|
*/
|
|
@@ -232,6 +276,7 @@ function prepare(target, root, type, props) {
|
|
|
232
276
|
object,
|
|
233
277
|
eventCount: 0,
|
|
234
278
|
handlers: {},
|
|
279
|
+
previousVisible: undefined,
|
|
235
280
|
isHidden: false
|
|
236
281
|
};
|
|
237
282
|
if (object) object.__r3f = instance;
|
|
@@ -415,6 +460,12 @@ function applyProps(object, props) {
|
|
|
415
460
|
// Ignore setting undefined props
|
|
416
461
|
// https://github.com/pmndrs/react-three-fiber/issues/274
|
|
417
462
|
if (value === undefined) continue;
|
|
463
|
+
|
|
464
|
+
// Update the visibility to restore without revealing a hidden instance.
|
|
465
|
+
if (prop === 'visible' && instance != null && instance.isHidden && instance.previousVisible !== undefined) {
|
|
466
|
+
instance.previousVisible = value;
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
418
469
|
let {
|
|
419
470
|
root,
|
|
420
471
|
key,
|
|
@@ -565,11 +616,24 @@ function swapInteractivity(store, object, newObject) {
|
|
|
565
616
|
internal.hovered.set(makeId(next), next);
|
|
566
617
|
}
|
|
567
618
|
});
|
|
619
|
+
|
|
620
|
+
// A capture is keyed by its eventObject, but it also carries the intersection that is replayed into
|
|
621
|
+
// every subsequent event. Both must follow the new instance, or the capture resolves to the discarded object.
|
|
568
622
|
internal.capturedMap.forEach(captures => {
|
|
569
|
-
const captureData
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
623
|
+
for (const [key, captureData] of Array.from(captures)) {
|
|
624
|
+
const {
|
|
625
|
+
intersection
|
|
626
|
+
} = captureData;
|
|
627
|
+
if (key !== object && intersection.object !== object && intersection.eventObject !== object) continue;
|
|
628
|
+
if (key === object) captures.delete(key);
|
|
629
|
+
captures.set(key === object ? newObject : key, {
|
|
630
|
+
...captureData,
|
|
631
|
+
intersection: {
|
|
632
|
+
...intersection,
|
|
633
|
+
object: intersection.object === object ? newObject : intersection.object,
|
|
634
|
+
eventObject: intersection.eventObject === object ? newObject : intersection.eventObject
|
|
635
|
+
}
|
|
636
|
+
});
|
|
573
637
|
}
|
|
574
638
|
});
|
|
575
639
|
}
|
|
@@ -968,7 +1032,109 @@ function createEvents(store) {
|
|
|
968
1032
|
};
|
|
969
1033
|
}
|
|
970
1034
|
|
|
1035
|
+
const isPromiseLike = value => typeof (value == null ? void 0 : value.then) === 'function';
|
|
1036
|
+
|
|
1037
|
+
/** A promise tagged with its state, the protocol React's `use` reads */
|
|
1038
|
+
|
|
1039
|
+
const fulfilled = value => Object.assign(Promise.resolve(value), {
|
|
1040
|
+
status: 'fulfilled',
|
|
1041
|
+
value
|
|
1042
|
+
});
|
|
1043
|
+
const rejected = reason => {
|
|
1044
|
+
const promise = Promise.reject(reason);
|
|
1045
|
+
promise.catch(() => {}); // the reason is read from `status`
|
|
1046
|
+
return Object.assign(promise, {
|
|
1047
|
+
status: 'rejected',
|
|
1048
|
+
reason
|
|
1049
|
+
});
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
/** A pending promise, settled once from outside. Its `status` updates the moment it settles */
|
|
1053
|
+
const deferred = () => {
|
|
1054
|
+
let resolve;
|
|
1055
|
+
let reject;
|
|
1056
|
+
const promise = new Promise((yes, no) => {
|
|
1057
|
+
resolve = value => {
|
|
1058
|
+
Object.assign(promise, {
|
|
1059
|
+
status: 'fulfilled',
|
|
1060
|
+
value
|
|
1061
|
+
});
|
|
1062
|
+
yes(value);
|
|
1063
|
+
};
|
|
1064
|
+
reject = reason => {
|
|
1065
|
+
Object.assign(promise, {
|
|
1066
|
+
status: 'rejected',
|
|
1067
|
+
reason
|
|
1068
|
+
});
|
|
1069
|
+
no(reason);
|
|
1070
|
+
};
|
|
1071
|
+
});
|
|
1072
|
+
promise.status = 'pending';
|
|
1073
|
+
promise.catch(() => {}); // the reason is read from `status`
|
|
1074
|
+
return {
|
|
1075
|
+
promise,
|
|
1076
|
+
resolve,
|
|
1077
|
+
reject
|
|
1078
|
+
};
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
/** Renderer creation and resource release. Root lifetime decides when release is safe. */
|
|
971
1082
|
const isRenderer = def => !!(def != null && def.render);
|
|
1083
|
+
|
|
1084
|
+
/** The renderer to configure, or a promise for one from an async factory */
|
|
1085
|
+
function createRenderer(canvas, gl) {
|
|
1086
|
+
const defaults = {
|
|
1087
|
+
canvas,
|
|
1088
|
+
powerPreference: 'high-performance',
|
|
1089
|
+
antialias: true,
|
|
1090
|
+
alpha: true
|
|
1091
|
+
};
|
|
1092
|
+
if (typeof gl === 'function') return gl(defaults);
|
|
1093
|
+
if (isRenderer(gl)) return gl;
|
|
1094
|
+
return new THREE.WebGLRenderer({
|
|
1095
|
+
...defaults,
|
|
1096
|
+
...gl
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* Frees a renderer R3F built. WebGLRenderer.dispose() releases programs and caches but keeps its
|
|
1102
|
+
* context until garbage collection, and browsers cap live WebGL contexts, so the context is lost
|
|
1103
|
+
* after it. A WebGPURenderer (from a factory) releases its device and context inside dispose().
|
|
1104
|
+
*/
|
|
1105
|
+
function disposeRenderer(gl) {
|
|
1106
|
+
// Avoid starting initialization through three's dispose(). A factory must await init()
|
|
1107
|
+
// before returning its renderer so readiness includes initialization and teardown can wait.
|
|
1108
|
+
if ((gl.hasInitialized == null ? void 0 : gl.hasInitialized()) === false) return;
|
|
1109
|
+
const disposed = attempt$1(() => {
|
|
1110
|
+
var _gl$renderLists;
|
|
1111
|
+
return gl.dispose ? gl.dispose() : (_gl$renderLists = gl.renderLists) == null ? void 0 : _gl$renderLists.dispose == null ? void 0 : _gl$renderLists.dispose();
|
|
1112
|
+
});
|
|
1113
|
+
// WebGPURenderer.dispose() is async from three r186
|
|
1114
|
+
if (isPromiseLike(disposed)) {
|
|
1115
|
+
return Promise.resolve(disposed).catch(error => console.warn('[R3F] Error disposing renderer', error)).then(() => attempt$1(() => gl.forceContextLoss == null ? void 0 : gl.forceContextLoss()));
|
|
1116
|
+
}
|
|
1117
|
+
attempt$1(() => gl.forceContextLoss == null ? void 0 : gl.forceContextLoss());
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/** Owned renderers are disposed. Caller-provided renderers release caches and their context. */
|
|
1121
|
+
function releaseRenderer(gl, owned) {
|
|
1122
|
+
if (!gl) return;
|
|
1123
|
+
if (owned) return disposeRenderer(gl);
|
|
1124
|
+
attempt$1(() => {
|
|
1125
|
+
var _gl$renderLists2;
|
|
1126
|
+
return (_gl$renderLists2 = gl.renderLists) == null ? void 0 : _gl$renderLists2.dispose == null ? void 0 : _gl$renderLists2.dispose();
|
|
1127
|
+
});
|
|
1128
|
+
attempt$1(() => gl.forceContextLoss == null ? void 0 : gl.forceContextLoss());
|
|
1129
|
+
}
|
|
1130
|
+
function attempt$1(step) {
|
|
1131
|
+
try {
|
|
1132
|
+
return step();
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
console.warn('[R3F] Error while unmounting root; teardown may be incomplete:', error);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
972
1138
|
const context = /* @__PURE__ */React.createContext(null);
|
|
973
1139
|
const createStore = (invalidate, advance) => {
|
|
974
1140
|
const rootStore = createWithEqualityFn((set, get) => {
|
|
@@ -1263,7 +1429,7 @@ function useGraph(object) {
|
|
|
1263
1429
|
return React.useMemo(() => buildGraph(object), [object]);
|
|
1264
1430
|
}
|
|
1265
1431
|
const memoizedLoaders = new WeakMap();
|
|
1266
|
-
const isConstructor
|
|
1432
|
+
const isConstructor = value => {
|
|
1267
1433
|
var _value$prototype;
|
|
1268
1434
|
return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
|
|
1269
1435
|
};
|
|
@@ -1272,7 +1438,7 @@ function loadingFn(extensions, onProgress) {
|
|
|
1272
1438
|
let loader;
|
|
1273
1439
|
|
|
1274
1440
|
// Construct and cache loader if constructor was passed
|
|
1275
|
-
if (isConstructor
|
|
1441
|
+
if (isConstructor(Proto)) {
|
|
1276
1442
|
loader = memoizedLoaders.get(Proto);
|
|
1277
1443
|
if (!loader) {
|
|
1278
1444
|
loader = new Proto();
|
|
@@ -1339,9 +1505,298 @@ useLoader.clear = function (loader, input) {
|
|
|
1339
1505
|
e = 2,
|
|
1340
1506
|
i$1 = 268435456;
|
|
1341
1507
|
|
|
1508
|
+
// Shim for OffscreenCanvas since it was removed from DOM types
|
|
1509
|
+
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54988
|
|
1510
|
+
|
|
1511
|
+
/** What a root has applied, so its next configuration applies only what changed */
|
|
1512
|
+
|
|
1513
|
+
const shallow = {
|
|
1514
|
+
objects: 'shallow'
|
|
1515
|
+
};
|
|
1516
|
+
function computeInitialSize(canvas, size) {
|
|
1517
|
+
if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
|
|
1518
|
+
const {
|
|
1519
|
+
width,
|
|
1520
|
+
height,
|
|
1521
|
+
top,
|
|
1522
|
+
left
|
|
1523
|
+
} = canvas.parentElement.getBoundingClientRect();
|
|
1524
|
+
return {
|
|
1525
|
+
width,
|
|
1526
|
+
height,
|
|
1527
|
+
top,
|
|
1528
|
+
left
|
|
1529
|
+
};
|
|
1530
|
+
} else if (!size && typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
|
|
1531
|
+
return {
|
|
1532
|
+
width: canvas.width,
|
|
1533
|
+
height: canvas.height,
|
|
1534
|
+
top: 0,
|
|
1535
|
+
left: 0
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
return {
|
|
1539
|
+
width: 0,
|
|
1540
|
+
height: 0,
|
|
1541
|
+
top: 0,
|
|
1542
|
+
left: 0,
|
|
1543
|
+
...size
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
/**
|
|
1548
|
+
* Applies the inputs that changed since the last configuration applied in full, to a root whose
|
|
1549
|
+
* renderer is installed. A runtime change, such as setFrameloop or an edit to gl.shadowMap, lasts
|
|
1550
|
+
* until its input changes. The pixel ratio instead follows the device on every call. Objects and
|
|
1551
|
+
* arrays compare shallowly, so an equal inline value is unchanged.
|
|
1552
|
+
*/
|
|
1553
|
+
function applyRootConfiguration({
|
|
1554
|
+
store,
|
|
1555
|
+
configuration
|
|
1556
|
+
}, canvas, props) {
|
|
1557
|
+
const {
|
|
1558
|
+
gl: glConfig,
|
|
1559
|
+
scene: sceneOptions,
|
|
1560
|
+
camera: cameraOptions,
|
|
1561
|
+
shadows = false,
|
|
1562
|
+
linear = false,
|
|
1563
|
+
flat = false,
|
|
1564
|
+
legacy = false,
|
|
1565
|
+
frameloop = 'always',
|
|
1566
|
+
dpr = [1, 2],
|
|
1567
|
+
performance,
|
|
1568
|
+
onPointerMissed
|
|
1569
|
+
} = props;
|
|
1570
|
+
const size = computeInitialSize(canvas, props.size);
|
|
1571
|
+
const next = {
|
|
1572
|
+
...props,
|
|
1573
|
+
shadows,
|
|
1574
|
+
linear,
|
|
1575
|
+
flat,
|
|
1576
|
+
legacy,
|
|
1577
|
+
frameloop,
|
|
1578
|
+
size
|
|
1579
|
+
};
|
|
1580
|
+
const state = store.getState();
|
|
1581
|
+
const gl = state.gl;
|
|
1582
|
+
const last = configuration.previous;
|
|
1583
|
+
const changed = key => !last || !is.equ(next[key], last[key], shallow);
|
|
1584
|
+
// A failure can leave inputs partly applied, so the next configuration applies all of them
|
|
1585
|
+
configuration.previous = undefined;
|
|
1586
|
+
|
|
1587
|
+
// Set up raycaster (one time only!)
|
|
1588
|
+
let raycaster = state.raycaster;
|
|
1589
|
+
if (!raycaster) state.set({
|
|
1590
|
+
raycaster: raycaster = new THREE.Raycaster()
|
|
1591
|
+
});
|
|
1592
|
+
if (changed('raycaster')) {
|
|
1593
|
+
var _props$raycaster;
|
|
1594
|
+
const {
|
|
1595
|
+
params,
|
|
1596
|
+
...options
|
|
1597
|
+
} = (_props$raycaster = props.raycaster) != null ? _props$raycaster : {};
|
|
1598
|
+
applyProps(raycaster, {
|
|
1599
|
+
...options,
|
|
1600
|
+
params: {
|
|
1601
|
+
...raycaster.params,
|
|
1602
|
+
...params
|
|
1603
|
+
}
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
// Create default camera, don't overwrite any user-set state
|
|
1608
|
+
if (!state.camera || state.camera === configuration.camera && changed('camera')) {
|
|
1609
|
+
configuration.camera = cameraOptions;
|
|
1610
|
+
const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
|
|
1611
|
+
const camera = isCamera ? cameraOptions : props.orthographic ? new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE.PerspectiveCamera(75, 0, 0.1, 1000);
|
|
1612
|
+
if (!isCamera) {
|
|
1613
|
+
camera.position.z = 5;
|
|
1614
|
+
if (cameraOptions) {
|
|
1615
|
+
applyProps(camera, cameraOptions);
|
|
1616
|
+
// Preserve user-defined frustum if possible
|
|
1617
|
+
// https://github.com/pmndrs/react-three-fiber/issues/3160
|
|
1618
|
+
if (!camera.manual) {
|
|
1619
|
+
if ('aspect' in cameraOptions || 'left' in cameraOptions || 'right' in cameraOptions || 'bottom' in cameraOptions || 'top' in cameraOptions) {
|
|
1620
|
+
camera.manual = true;
|
|
1621
|
+
camera.updateProjectionMatrix();
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
// Always look at center by default
|
|
1626
|
+
if (!state.camera && !(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
|
|
1627
|
+
}
|
|
1628
|
+
state.set({
|
|
1629
|
+
camera
|
|
1630
|
+
});
|
|
1631
|
+
|
|
1632
|
+
// Configure raycaster
|
|
1633
|
+
// https://github.com/pmndrs/react-xr/issues/300
|
|
1634
|
+
raycaster.camera = camera;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// Set up scene (one time only!)
|
|
1638
|
+
if (!state.scene) {
|
|
1639
|
+
let scene;
|
|
1640
|
+
if (sceneOptions != null && sceneOptions.isScene) {
|
|
1641
|
+
scene = sceneOptions;
|
|
1642
|
+
prepare(scene, store, '', {});
|
|
1643
|
+
} else {
|
|
1644
|
+
scene = new THREE.Scene();
|
|
1645
|
+
prepare(scene, store, '', {});
|
|
1646
|
+
if (sceneOptions) applyProps(scene, sceneOptions);
|
|
1647
|
+
}
|
|
1648
|
+
state.set({
|
|
1649
|
+
scene
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
// Store events internally
|
|
1654
|
+
if (props.events && !state.events.handlers) state.set({
|
|
1655
|
+
events: props.events(store)
|
|
1656
|
+
});
|
|
1657
|
+
if (changed('size')) state.setSize(size.width, size.height, size.top, size.left);
|
|
1658
|
+
// The ratio follows window.devicePixelRatio, which changes with browser zoom or another display
|
|
1659
|
+
if (state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
|
|
1660
|
+
if (changed('frameloop')) state.setFrameloop(frameloop);
|
|
1661
|
+
if (changed('onPointerMissed')) state.set({
|
|
1662
|
+
onPointerMissed
|
|
1663
|
+
});
|
|
1664
|
+
// Performance options merge, so removing them keeps the current settings
|
|
1665
|
+
if (performance && changed('performance')) {
|
|
1666
|
+
state.set(state => ({
|
|
1667
|
+
performance: {
|
|
1668
|
+
...state.performance,
|
|
1669
|
+
...performance
|
|
1670
|
+
}
|
|
1671
|
+
}));
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
// Set up XR (one time only!)
|
|
1675
|
+
if (!state.xr) {
|
|
1676
|
+
var _gl$xr;
|
|
1677
|
+
// Handle frame behavior in WebXR
|
|
1678
|
+
const handleXRFrame = (timestamp, frame) => {
|
|
1679
|
+
const state = store.getState();
|
|
1680
|
+
if (state.frameloop === 'never') return;
|
|
1681
|
+
advance(timestamp, true, state, frame);
|
|
1682
|
+
};
|
|
1683
|
+
|
|
1684
|
+
// Toggle render switching on session
|
|
1685
|
+
const handleSessionChange = () => {
|
|
1686
|
+
const state = store.getState();
|
|
1687
|
+
state.gl.xr.enabled = state.gl.xr.isPresenting;
|
|
1688
|
+
state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
|
|
1689
|
+
if (!state.gl.xr.isPresenting) invalidate(state);
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
// WebXR session manager
|
|
1693
|
+
const xr = {
|
|
1694
|
+
connect() {
|
|
1695
|
+
const gl = store.getState().gl;
|
|
1696
|
+
gl.xr.addEventListener('sessionstart', handleSessionChange);
|
|
1697
|
+
gl.xr.addEventListener('sessionend', handleSessionChange);
|
|
1698
|
+
},
|
|
1699
|
+
disconnect() {
|
|
1700
|
+
const gl = store.getState().gl;
|
|
1701
|
+
gl.xr.removeEventListener('sessionstart', handleSessionChange);
|
|
1702
|
+
gl.xr.removeEventListener('sessionend', handleSessionChange);
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
|
|
1706
|
+
// Subscribe to WebXR session events
|
|
1707
|
+
if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
|
|
1708
|
+
state.set({
|
|
1709
|
+
xr
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
// Set shadowmap
|
|
1714
|
+
if (gl.shadowMap && changed('shadows')) {
|
|
1715
|
+
const oldEnabled = gl.shadowMap.enabled;
|
|
1716
|
+
const oldType = gl.shadowMap.type;
|
|
1717
|
+
gl.shadowMap.enabled = !!shadows;
|
|
1718
|
+
if (is.boo(shadows)) {
|
|
1719
|
+
gl.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
1720
|
+
} else if (is.str(shadows)) {
|
|
1721
|
+
var _types$shadows;
|
|
1722
|
+
const types = {
|
|
1723
|
+
basic: THREE.BasicShadowMap,
|
|
1724
|
+
percentage: THREE.PCFShadowMap,
|
|
1725
|
+
soft: THREE.PCFSoftShadowMap,
|
|
1726
|
+
variance: THREE.VSMShadowMap
|
|
1727
|
+
};
|
|
1728
|
+
gl.shadowMap.type = (_types$shadows = types[shadows]) != null ? _types$shadows : THREE.PCFSoftShadowMap;
|
|
1729
|
+
} else if (is.obj(shadows)) {
|
|
1730
|
+
Object.assign(gl.shadowMap, shadows);
|
|
1731
|
+
}
|
|
1732
|
+
if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// Explicit renderer options take precedence over the color shorthands
|
|
1736
|
+
const glProps = glConfig && !is.fun(glConfig) && !isRenderer(glConfig) ? glConfig : undefined;
|
|
1737
|
+
if (changed('legacy')) {
|
|
1738
|
+
THREE.ColorManagement.enabled = !legacy;
|
|
1739
|
+
state.set({
|
|
1740
|
+
legacy
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
if (changed('linear')) {
|
|
1744
|
+
if ((glProps == null ? void 0 : glProps.outputColorSpace) === undefined) {
|
|
1745
|
+
gl.outputColorSpace = linear ? THREE.LinearSRGBColorSpace : THREE.SRGBColorSpace;
|
|
1746
|
+
}
|
|
1747
|
+
state.set({
|
|
1748
|
+
linear
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
if (changed('flat')) {
|
|
1752
|
+
if ((glProps == null ? void 0 : glProps.toneMapping) === undefined) gl.toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;
|
|
1753
|
+
state.set({
|
|
1754
|
+
flat
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
// Set gl props
|
|
1759
|
+
if (glProps && changed('gl')) applyProps(gl, glProps);
|
|
1760
|
+
configuration.previous = next;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
/**
|
|
1764
|
+
* Moving to another display or zooming changes window.devicePixelRatio without resizing the canvas,
|
|
1765
|
+
* so nothing configures the root again. Resolves the ratio the way configuration does, from the last
|
|
1766
|
+
* applied dpr. Returns the function that stops following.
|
|
1767
|
+
*/
|
|
1768
|
+
function followDpr(root) {
|
|
1769
|
+
const xr = () => {
|
|
1770
|
+
var _root$store$getState$;
|
|
1771
|
+
return (_root$store$getState$ = root.store.getState().gl) == null ? void 0 : _root$store$getState$.xr;
|
|
1772
|
+
};
|
|
1773
|
+
const follow = () => {
|
|
1774
|
+
var _xr, _xr2, _xr3, _last$dpr;
|
|
1775
|
+
const state = root.store.getState();
|
|
1776
|
+
// three doesn't resize during XR and restores its own pixel ratio once the session ends. It holds
|
|
1777
|
+
// the session before it sets isPresenting. Listeners are deduplicated, so this waits once
|
|
1778
|
+
if ((_xr = xr()) != null && _xr.isPresenting || (_xr2 = xr()) != null && _xr2.getSession != null && _xr2.getSession()) {
|
|
1779
|
+
xr().addEventListener('sessionend', follow);
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
(_xr3 = xr()) == null ? void 0 : _xr3.removeEventListener == null ? void 0 : _xr3.removeEventListener('sessionend', follow);
|
|
1783
|
+
// A failed configuration applies everything on the next one
|
|
1784
|
+
const last = root.configuration.previous;
|
|
1785
|
+
if (!last) return;
|
|
1786
|
+
const dpr = (_last$dpr = last.dpr) != null ? _last$dpr : [1, 2];
|
|
1787
|
+
if (state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
|
|
1788
|
+
};
|
|
1789
|
+
const unwatch = watchDpr(follow);
|
|
1790
|
+
return () => {
|
|
1791
|
+
var _xr4;
|
|
1792
|
+
unwatch();
|
|
1793
|
+
(_xr4 = xr()) == null ? void 0 : _xr4.removeEventListener == null ? void 0 : _xr4.removeEventListener('sessionend', follow);
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1342
1797
|
var packageData = {
|
|
1343
1798
|
name: "@react-three/fiber",
|
|
1344
|
-
version: "9.8.
|
|
1799
|
+
version: "9.8.2",
|
|
1345
1800
|
description: "A React renderer for Threejs",
|
|
1346
1801
|
keywords: [
|
|
1347
1802
|
"react",
|
|
@@ -1373,6 +1828,12 @@ var packageData = {
|
|
|
1373
1828
|
module: "dist/react-three-fiber.esm.js",
|
|
1374
1829
|
types: "dist/react-three-fiber.cjs.d.ts",
|
|
1375
1830
|
"react-native": "native/dist/react-three-fiber-native.cjs.js",
|
|
1831
|
+
files: [
|
|
1832
|
+
"dist",
|
|
1833
|
+
"native",
|
|
1834
|
+
"react-reconciler",
|
|
1835
|
+
"CHANGELOG.md"
|
|
1836
|
+
],
|
|
1376
1837
|
sideEffects: false,
|
|
1377
1838
|
preconstruct: {
|
|
1378
1839
|
entrypoints: [
|
|
@@ -1392,7 +1853,7 @@ var packageData = {
|
|
|
1392
1853
|
"@types/webxr": "*",
|
|
1393
1854
|
"base64-js": "^1.5.1",
|
|
1394
1855
|
buffer: "^6.0.3",
|
|
1395
|
-
"its-fine": "^2.
|
|
1856
|
+
"its-fine": "^2.1.1",
|
|
1396
1857
|
"react-use-measure": "^2.1.7",
|
|
1397
1858
|
scheduler: "^0.28.0",
|
|
1398
1859
|
"suspend-react": "^0.1.3",
|
|
@@ -16113,6 +16574,27 @@ function G1() {
|
|
|
16113
16574
|
var J1 = G1();
|
|
16114
16575
|
const Z1 = $1(J1);
|
|
16115
16576
|
|
|
16577
|
+
/** Constructor registration and lookup for JSX elements. */
|
|
16578
|
+
|
|
16579
|
+
const catalogue = {};
|
|
16580
|
+
let i = 0;
|
|
16581
|
+
const toPascalCase = type => `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
16582
|
+
function hasConstructor(name) {
|
|
16583
|
+
return name in catalogue;
|
|
16584
|
+
}
|
|
16585
|
+
function resolveConstructor(name) {
|
|
16586
|
+
return catalogue[name];
|
|
16587
|
+
}
|
|
16588
|
+
function extend(objects) {
|
|
16589
|
+
if (typeof objects === 'function') {
|
|
16590
|
+
const Component = `${i++}`;
|
|
16591
|
+
catalogue[Component] = objects;
|
|
16592
|
+
return Component;
|
|
16593
|
+
} else {
|
|
16594
|
+
Object.assign(catalogue, objects);
|
|
16595
|
+
}
|
|
16596
|
+
}
|
|
16597
|
+
|
|
16116
16598
|
function createReconciler(config) {
|
|
16117
16599
|
const reconciler = Z1(config);
|
|
16118
16600
|
|
|
@@ -16126,24 +16608,11 @@ const NoEventPriority = 0;
|
|
|
16126
16608
|
// https://github.com/pmndrs/react-three-fiber/pull/2931
|
|
16127
16609
|
// https://github.com/microsoft/TypeScript/issues/37079
|
|
16128
16610
|
|
|
16129
|
-
const catalogue = {};
|
|
16130
16611
|
const PREFIX_REGEX = /^three(?=[A-Z])/;
|
|
16131
|
-
const toPascalCase = type => `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
16132
|
-
let i = 0;
|
|
16133
|
-
const isConstructor = object => typeof object === 'function';
|
|
16134
|
-
function extend(objects) {
|
|
16135
|
-
if (isConstructor(objects)) {
|
|
16136
|
-
const Component = `${i++}`;
|
|
16137
|
-
catalogue[Component] = objects;
|
|
16138
|
-
return Component;
|
|
16139
|
-
} else {
|
|
16140
|
-
Object.assign(catalogue, objects);
|
|
16141
|
-
}
|
|
16142
|
-
}
|
|
16143
16612
|
function validateInstance(type, props) {
|
|
16144
16613
|
// Get target from catalogue
|
|
16145
16614
|
const name = toPascalCase(type);
|
|
16146
|
-
const target =
|
|
16615
|
+
const target = resolveConstructor(name);
|
|
16147
16616
|
|
|
16148
16617
|
// Validate element target
|
|
16149
16618
|
if (type !== 'primitive' && !target) throw new Error(`R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`);
|
|
@@ -16157,7 +16626,7 @@ function validateInstance(type, props) {
|
|
|
16157
16626
|
function createInstance(type, props, root) {
|
|
16158
16627
|
var _props$object;
|
|
16159
16628
|
// Remove three* prefix from elements if native element not present
|
|
16160
|
-
type = toPascalCase(type)
|
|
16629
|
+
type = hasConstructor(toPascalCase(type)) ? type : type.replace(PREFIX_REGEX, '');
|
|
16161
16630
|
validateInstance(type, props);
|
|
16162
16631
|
|
|
16163
16632
|
// Regenerate the R3F instance for primitives to simulate a new object
|
|
@@ -16170,6 +16639,7 @@ function hideInstance(instance) {
|
|
|
16170
16639
|
if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
|
|
16171
16640
|
detach(instance.parent, instance);
|
|
16172
16641
|
} else if (isObject3D(instance.object)) {
|
|
16642
|
+
instance.previousVisible = instance.object.visible;
|
|
16173
16643
|
instance.object.visible = false;
|
|
16174
16644
|
}
|
|
16175
16645
|
instance.isHidden = true;
|
|
@@ -16181,9 +16651,11 @@ function unhideInstance(instance) {
|
|
|
16181
16651
|
var _instance$parent2;
|
|
16182
16652
|
if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
|
|
16183
16653
|
attach(instance.parent, instance);
|
|
16184
|
-
} else if (isObject3D(instance.object)
|
|
16185
|
-
|
|
16654
|
+
} else if (isObject3D(instance.object)) {
|
|
16655
|
+
var _instance$previousVis;
|
|
16656
|
+
instance.object.visible = (_instance$previousVis = instance.previousVisible) != null ? _instance$previousVis : true;
|
|
16186
16657
|
}
|
|
16658
|
+
instance.previousVisible = undefined;
|
|
16187
16659
|
instance.isHidden = false;
|
|
16188
16660
|
invalidateInstance(instance);
|
|
16189
16661
|
}
|
|
@@ -16201,7 +16673,7 @@ function handleContainerEffects(parent, child, beforeChild) {
|
|
|
16201
16673
|
if (!child.object) {
|
|
16202
16674
|
var _child$props$object, _child$props$args;
|
|
16203
16675
|
// Get target from catalogue
|
|
16204
|
-
const target =
|
|
16676
|
+
const target = resolveConstructor(toPascalCase(child.type));
|
|
16205
16677
|
|
|
16206
16678
|
// Create object
|
|
16207
16679
|
child.object = (_child$props$object = child.props.object) != null ? _child$props$object : new target(...((_child$props$args = child.props.args) != null ? _child$props$args : []));
|
|
@@ -16298,6 +16770,9 @@ function disposeOnIdle(object) {
|
|
|
16298
16770
|
function removeChild(parent, child, dispose) {
|
|
16299
16771
|
if (!child) return;
|
|
16300
16772
|
|
|
16773
|
+
// Unlinking clears the parent, which invalidateInstance checks, so remember it
|
|
16774
|
+
const wasLinked = !!child.parent;
|
|
16775
|
+
|
|
16301
16776
|
// Unlink instances
|
|
16302
16777
|
child.parent = null;
|
|
16303
16778
|
const childIndex = parent.children.indexOf(child);
|
|
@@ -16335,7 +16810,11 @@ function removeChild(parent, child, dispose) {
|
|
|
16335
16810
|
}
|
|
16336
16811
|
|
|
16337
16812
|
// Tree was updated, request a frame for top-level instance
|
|
16338
|
-
if (dispose === undefined)
|
|
16813
|
+
if (dispose === undefined && wasLinked) {
|
|
16814
|
+
var _child$root;
|
|
16815
|
+
const state = (_child$root = child.root) == null ? void 0 : _child$root.getState == null ? void 0 : _child$root.getState();
|
|
16816
|
+
if (state && state.internal.frames === 0) state.invalidate();
|
|
16817
|
+
}
|
|
16339
16818
|
}
|
|
16340
16819
|
function setFiberRef(fiber, publicInstance) {
|
|
16341
16820
|
for (const _fiber of [fiber, fiber.alternate]) {
|
|
@@ -16396,7 +16875,7 @@ function swapReconstructedInstances() {
|
|
|
16396
16875
|
if (parent) {
|
|
16397
16876
|
var _instance$props$objec, _instance$props$args;
|
|
16398
16877
|
// Get target from catalogue
|
|
16399
|
-
const target =
|
|
16878
|
+
const target = resolveConstructor(toPascalCase(instance.type));
|
|
16400
16879
|
|
|
16401
16880
|
// Create object
|
|
16402
16881
|
const prevObject = instance.object;
|
|
@@ -16728,76 +17207,49 @@ const reconciler = /* @__PURE__ */createReconciler({
|
|
|
16728
17207
|
getSuspendedCommitReason: (_state, _rootContainer) => null
|
|
16729
17208
|
});
|
|
16730
17209
|
|
|
16731
|
-
|
|
16732
|
-
|
|
16733
|
-
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
|
|
16739
|
-
|
|
16740
|
-
|
|
16741
|
-
promise.catch(() => {}); // the reason is read from `status`
|
|
16742
|
-
return Object.assign(promise, {
|
|
16743
|
-
status: 'rejected',
|
|
16744
|
-
reason
|
|
16745
|
-
});
|
|
16746
|
-
};
|
|
16747
|
-
|
|
16748
|
-
/** Tags `promise` in place once it settles. */
|
|
16749
|
-
const tracked = promise => {
|
|
16750
|
-
const result = Promise.resolve(promise);
|
|
16751
|
-
result.status = 'pending';
|
|
16752
|
-
// Statement bodies: returning `result` would make this derived promise adopt its rejection
|
|
16753
|
-
result.then(value => {
|
|
16754
|
-
Object.assign(result, {
|
|
16755
|
-
status: 'fulfilled',
|
|
16756
|
-
value
|
|
16757
|
-
});
|
|
16758
|
-
}, reason => {
|
|
16759
|
-
Object.assign(result, {
|
|
16760
|
-
status: 'rejected',
|
|
16761
|
-
reason
|
|
16762
|
-
});
|
|
16763
|
-
});
|
|
16764
|
-
return result;
|
|
16765
|
-
};
|
|
17210
|
+
/**
|
|
17211
|
+
* Force React to flush any updates inside the provided callback synchronously and immediately.
|
|
17212
|
+
* All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync).
|
|
17213
|
+
* Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without
|
|
17214
|
+
* having to revert to a non-React solution. Note: this will only flush updates within the `Canvas` root.
|
|
17215
|
+
*/
|
|
17216
|
+
function flushSync(fn) {
|
|
17217
|
+
// @ts-ignore - reconciler types are not maintained
|
|
17218
|
+
return reconciler.flushSyncFromReconciler(fn);
|
|
17219
|
+
}
|
|
16766
17220
|
|
|
16767
17221
|
const _roots = new Map();
|
|
16768
|
-
|
|
16769
|
-
|
|
16770
|
-
|
|
16771
|
-
|
|
16772
|
-
|
|
16773
|
-
|
|
16774
|
-
|
|
16775
|
-
|
|
16776
|
-
|
|
16777
|
-
|
|
16778
|
-
|
|
16779
|
-
|
|
16780
|
-
|
|
16781
|
-
|
|
16782
|
-
|
|
16783
|
-
|
|
16784
|
-
|
|
16785
|
-
|
|
16786
|
-
|
|
16787
|
-
|
|
16788
|
-
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
|
|
16792
|
-
|
|
17222
|
+
/**
|
|
17223
|
+
* open -> closing -> disposed
|
|
17224
|
+
* | use
|
|
17225
|
+
* v
|
|
17226
|
+
* open
|
|
17227
|
+
*
|
|
17228
|
+
* Only closing can be cancelled. Each unmount carries a token, so only the latest one can dispose.
|
|
17229
|
+
*/
|
|
17230
|
+
function transitionRoot(root, event) {
|
|
17231
|
+
const state = root.state;
|
|
17232
|
+
switch (event.type) {
|
|
17233
|
+
case 'use':
|
|
17234
|
+
if (state.status === 'disposed') return false;
|
|
17235
|
+
root.state = {
|
|
17236
|
+
status: 'open'
|
|
17237
|
+
};
|
|
17238
|
+
return true;
|
|
17239
|
+
case 'unmount':
|
|
17240
|
+
if (state.status === 'disposed') return false;
|
|
17241
|
+
root.state = {
|
|
17242
|
+
status: 'closing',
|
|
17243
|
+
token: event.token
|
|
17244
|
+
};
|
|
17245
|
+
return true;
|
|
17246
|
+
case 'dispose':
|
|
17247
|
+
if (state.status !== 'closing' || state.token !== event.token) return false;
|
|
17248
|
+
root.state = {
|
|
17249
|
+
status: 'disposed'
|
|
17250
|
+
};
|
|
17251
|
+
return true;
|
|
16793
17252
|
}
|
|
16794
|
-
return {
|
|
16795
|
-
width: 0,
|
|
16796
|
-
height: 0,
|
|
16797
|
-
top: 0,
|
|
16798
|
-
left: 0,
|
|
16799
|
-
...size
|
|
16800
|
-
};
|
|
16801
17253
|
}
|
|
16802
17254
|
function createRoot(canvas) {
|
|
16803
17255
|
// Check against mistaken use of createRoot
|
|
@@ -16842,286 +17294,86 @@ function createRoot(canvas) {
|
|
|
16842
17294
|
const root = prevRoot || {
|
|
16843
17295
|
fiber,
|
|
16844
17296
|
store,
|
|
16845
|
-
|
|
16846
|
-
|
|
17297
|
+
state: {
|
|
17298
|
+
status: 'open'
|
|
17299
|
+
},
|
|
17300
|
+
ready: fulfilled(undefined),
|
|
17301
|
+
configuration: {},
|
|
17302
|
+
ownsRenderer: false
|
|
16847
17303
|
};
|
|
16848
17304
|
if (!prevRoot) _roots.set(canvas, root);
|
|
16849
|
-
|
|
16850
|
-
// Locals
|
|
16851
|
-
let onCreated;
|
|
16852
|
-
let lastCamera;
|
|
16853
17305
|
let mounted = false;
|
|
16854
17306
|
return {
|
|
16855
17307
|
get ready() {
|
|
16856
17308
|
return root.ready;
|
|
16857
17309
|
},
|
|
16858
17310
|
configure(props = {}) {
|
|
16859
|
-
root
|
|
16860
|
-
|
|
16861
|
-
|
|
16862
|
-
|
|
16863
|
-
|
|
16864
|
-
events,
|
|
16865
|
-
onCreated: onCreatedCallback,
|
|
16866
|
-
shadows = false,
|
|
16867
|
-
linear = false,
|
|
16868
|
-
flat = false,
|
|
16869
|
-
legacy = false,
|
|
16870
|
-
orthographic = false,
|
|
16871
|
-
frameloop = 'always',
|
|
16872
|
-
dpr = [1, 2],
|
|
16873
|
-
performance,
|
|
16874
|
-
raycaster: raycastOptions,
|
|
16875
|
-
camera: cameraOptions,
|
|
16876
|
-
onPointerMissed
|
|
16877
|
-
} = props;
|
|
16878
|
-
const defaultProps = {
|
|
16879
|
-
canvas: canvas,
|
|
16880
|
-
powerPreference: 'high-performance',
|
|
16881
|
-
antialias: true,
|
|
16882
|
-
alpha: true
|
|
16883
|
-
};
|
|
17311
|
+
if (!transitionRoot(root, {
|
|
17312
|
+
type: 'use'
|
|
17313
|
+
})) {
|
|
17314
|
+
return rejected(new Error('R3F: Cannot configure a root after disposal has started.'));
|
|
17315
|
+
}
|
|
16884
17316
|
|
|
16885
|
-
//
|
|
16886
|
-
const
|
|
17317
|
+
// Configuration applies in call order. Publishing first queues calls made while this one runs
|
|
17318
|
+
const previous = root.ready;
|
|
17319
|
+
const {
|
|
17320
|
+
promise,
|
|
17321
|
+
resolve,
|
|
17322
|
+
reject
|
|
17323
|
+
} = deferred();
|
|
17324
|
+
root.ready = promise;
|
|
17325
|
+
const apply = gl => {
|
|
16887
17326
|
const state = store.getState();
|
|
16888
|
-
|
|
16889
17327
|
// Set up renderer (one time only!)
|
|
16890
|
-
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
|
|
16894
|
-
...defaultProps,
|
|
16895
|
-
...glConfig
|
|
16896
|
-
});
|
|
16897
|
-
state.set({
|
|
16898
|
-
gl
|
|
16899
|
-
});
|
|
16900
|
-
}
|
|
16901
|
-
|
|
16902
|
-
// Set up raycaster (one time only!)
|
|
16903
|
-
let raycaster = state.raycaster;
|
|
16904
|
-
if (!raycaster) state.set({
|
|
16905
|
-
raycaster: raycaster = new THREE.Raycaster()
|
|
16906
|
-
});
|
|
16907
|
-
|
|
16908
|
-
// Set raycaster options
|
|
16909
|
-
const {
|
|
16910
|
-
params,
|
|
16911
|
-
...options
|
|
16912
|
-
} = raycastOptions || {};
|
|
16913
|
-
if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, {
|
|
16914
|
-
...options
|
|
16915
|
-
});
|
|
16916
|
-
if (!is.equ(params, raycaster.params, shallowLoose)) applyProps(raycaster, {
|
|
16917
|
-
params: {
|
|
16918
|
-
...raycaster.params,
|
|
16919
|
-
...params
|
|
16920
|
-
}
|
|
16921
|
-
});
|
|
16922
|
-
|
|
16923
|
-
// Create default camera, don't overwrite any user-set state
|
|
16924
|
-
if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
|
|
16925
|
-
lastCamera = cameraOptions;
|
|
16926
|
-
const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
|
|
16927
|
-
const camera = isCamera ? cameraOptions : orthographic ? new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE.PerspectiveCamera(75, 0, 0.1, 1000);
|
|
16928
|
-
if (!isCamera) {
|
|
16929
|
-
camera.position.z = 5;
|
|
16930
|
-
if (cameraOptions) {
|
|
16931
|
-
applyProps(camera, cameraOptions);
|
|
16932
|
-
// Preserve user-defined frustum if possible
|
|
16933
|
-
// https://github.com/pmndrs/react-three-fiber/issues/3160
|
|
16934
|
-
if (!camera.manual) {
|
|
16935
|
-
if ('aspect' in cameraOptions || 'left' in cameraOptions || 'right' in cameraOptions || 'bottom' in cameraOptions || 'top' in cameraOptions) {
|
|
16936
|
-
camera.manual = true;
|
|
16937
|
-
camera.updateProjectionMatrix();
|
|
16938
|
-
}
|
|
16939
|
-
}
|
|
16940
|
-
}
|
|
16941
|
-
// Always look at center by default
|
|
16942
|
-
if (!state.camera && !(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
|
|
16943
|
-
}
|
|
16944
|
-
state.set({
|
|
16945
|
-
camera
|
|
16946
|
-
});
|
|
16947
|
-
|
|
16948
|
-
// Configure raycaster
|
|
16949
|
-
// https://github.com/pmndrs/react-xr/issues/300
|
|
16950
|
-
raycaster.camera = camera;
|
|
16951
|
-
}
|
|
16952
|
-
|
|
16953
|
-
// Set up scene (one time only!)
|
|
16954
|
-
if (!state.scene) {
|
|
16955
|
-
let scene;
|
|
16956
|
-
if (sceneOptions != null && sceneOptions.isScene) {
|
|
16957
|
-
scene = sceneOptions;
|
|
16958
|
-
prepare(scene, store, '', {});
|
|
16959
|
-
} else {
|
|
16960
|
-
scene = new THREE.Scene();
|
|
16961
|
-
prepare(scene, store, '', {});
|
|
16962
|
-
if (sceneOptions) applyProps(scene, sceneOptions);
|
|
16963
|
-
}
|
|
16964
|
-
state.set({
|
|
16965
|
-
scene
|
|
16966
|
-
});
|
|
16967
|
-
}
|
|
16968
|
-
|
|
16969
|
-
// Store events internally
|
|
16970
|
-
if (events && !state.events.handlers) state.set({
|
|
16971
|
-
events: events(store)
|
|
16972
|
-
});
|
|
16973
|
-
// Check size, allow it to take on container bounds initially
|
|
16974
|
-
const size = computeInitialSize(canvas, propsSize);
|
|
16975
|
-
if (!is.equ(size, state.size, shallowLoose)) {
|
|
16976
|
-
state.setSize(size.width, size.height, size.top, size.left);
|
|
16977
|
-
}
|
|
16978
|
-
// Check pixelratio
|
|
16979
|
-
if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
|
|
16980
|
-
// Check frameloop
|
|
16981
|
-
if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
|
|
16982
|
-
// Check pointer missed
|
|
16983
|
-
if (!state.onPointerMissed) state.set({
|
|
16984
|
-
onPointerMissed
|
|
16985
|
-
});
|
|
16986
|
-
// Check performance
|
|
16987
|
-
if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
|
|
16988
|
-
performance: {
|
|
16989
|
-
...state.performance,
|
|
16990
|
-
...performance
|
|
16991
|
-
}
|
|
16992
|
-
}));
|
|
16993
|
-
|
|
16994
|
-
// Set up XR (one time only!)
|
|
16995
|
-
if (!state.xr) {
|
|
16996
|
-
var _gl$xr;
|
|
16997
|
-
// Handle frame behavior in WebXR
|
|
16998
|
-
const handleXRFrame = (timestamp, frame) => {
|
|
16999
|
-
const state = store.getState();
|
|
17000
|
-
if (state.frameloop === 'never') return;
|
|
17001
|
-
advance(timestamp, true, state, frame);
|
|
17002
|
-
};
|
|
17003
|
-
|
|
17004
|
-
// Toggle render switching on session
|
|
17005
|
-
const handleSessionChange = () => {
|
|
17006
|
-
const state = store.getState();
|
|
17007
|
-
state.gl.xr.enabled = state.gl.xr.isPresenting;
|
|
17008
|
-
state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
|
|
17009
|
-
if (!state.gl.xr.isPresenting) invalidate(state);
|
|
17010
|
-
};
|
|
17011
|
-
|
|
17012
|
-
// WebXR session manager
|
|
17013
|
-
const xr = {
|
|
17014
|
-
connect() {
|
|
17015
|
-
const gl = store.getState().gl;
|
|
17016
|
-
gl.xr.addEventListener('sessionstart', handleSessionChange);
|
|
17017
|
-
gl.xr.addEventListener('sessionend', handleSessionChange);
|
|
17018
|
-
},
|
|
17019
|
-
disconnect() {
|
|
17020
|
-
const gl = store.getState().gl;
|
|
17021
|
-
gl.xr.removeEventListener('sessionstart', handleSessionChange);
|
|
17022
|
-
gl.xr.removeEventListener('sessionend', handleSessionChange);
|
|
17023
|
-
}
|
|
17024
|
-
};
|
|
17025
|
-
|
|
17026
|
-
// Subscribe to WebXR session events
|
|
17027
|
-
if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
|
|
17328
|
+
if (!state.gl) {
|
|
17329
|
+
// R3F owns what it builds, a factory's result included, since it calls the factory
|
|
17330
|
+
// once per root. A renderer instance belongs to the caller
|
|
17331
|
+
root.ownsRenderer = !isRenderer(props.gl);
|
|
17028
17332
|
state.set({
|
|
17029
|
-
|
|
17333
|
+
gl: gl
|
|
17030
17334
|
});
|
|
17031
17335
|
}
|
|
17032
|
-
|
|
17033
|
-
//
|
|
17034
|
-
if (
|
|
17035
|
-
|
|
17036
|
-
const oldType = gl.shadowMap.type;
|
|
17037
|
-
gl.shadowMap.enabled = !!shadows;
|
|
17038
|
-
if (is.boo(shadows)) {
|
|
17039
|
-
gl.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
17040
|
-
} else if (is.str(shadows)) {
|
|
17041
|
-
var _types$shadows;
|
|
17042
|
-
const types = {
|
|
17043
|
-
basic: THREE.BasicShadowMap,
|
|
17044
|
-
percentage: THREE.PCFShadowMap,
|
|
17045
|
-
soft: THREE.PCFSoftShadowMap,
|
|
17046
|
-
variance: THREE.VSMShadowMap
|
|
17047
|
-
};
|
|
17048
|
-
gl.shadowMap.type = (_types$shadows = types[shadows]) != null ? _types$shadows : THREE.PCFSoftShadowMap;
|
|
17049
|
-
} else if (is.obj(shadows)) {
|
|
17050
|
-
Object.assign(gl.shadowMap, shadows);
|
|
17051
|
-
}
|
|
17052
|
-
if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
|
|
17053
|
-
}
|
|
17054
|
-
THREE.ColorManagement.enabled = !legacy;
|
|
17055
|
-
|
|
17056
|
-
// Set color space and tonemapping preferences
|
|
17057
|
-
if (first) {
|
|
17058
|
-
gl.outputColorSpace = linear ? THREE.LinearSRGBColorSpace : THREE.SRGBColorSpace;
|
|
17059
|
-
gl.toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;
|
|
17060
|
-
}
|
|
17061
|
-
|
|
17062
|
-
// Update color management state
|
|
17063
|
-
if (state.legacy !== legacy) state.set(() => ({
|
|
17064
|
-
legacy
|
|
17065
|
-
}));
|
|
17066
|
-
if (state.linear !== linear) state.set(() => ({
|
|
17067
|
-
linear
|
|
17068
|
-
}));
|
|
17069
|
-
if (state.flat !== flat) state.set(() => ({
|
|
17070
|
-
flat
|
|
17071
|
-
}));
|
|
17072
|
-
|
|
17073
|
-
// Set gl props
|
|
17074
|
-
if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig);
|
|
17075
|
-
|
|
17076
|
-
// Set locals
|
|
17077
|
-
onCreated = onCreatedCallback;
|
|
17078
|
-
};
|
|
17079
|
-
const fail = error => {
|
|
17080
|
-
const failure = rejected(error);
|
|
17081
|
-
root.ready = failure;
|
|
17082
|
-
return failure;
|
|
17336
|
+
applyRootConfiguration(root, canvas, props);
|
|
17337
|
+
// Only a range, including the default one, follows the display: a fixed dpr never touches matchMedia
|
|
17338
|
+
if (!root.unwatchDpr && (props.dpr === undefined || Array.isArray(props.dpr))) root.unwatchDpr = followDpr(root);
|
|
17339
|
+
resolve(this);
|
|
17083
17340
|
};
|
|
17084
|
-
const run =
|
|
17341
|
+
const run = () => {
|
|
17085
17342
|
try {
|
|
17086
|
-
|
|
17087
|
-
|
|
17088
|
-
|
|
17343
|
+
var _store$getState$gl;
|
|
17344
|
+
const gl = (_store$getState$gl = store.getState().gl) != null ? _store$getState$gl : createRenderer(canvas, props.gl);
|
|
17345
|
+
// Only an async renderer factory makes configuration asynchronous
|
|
17346
|
+
if (isPromiseLike(gl)) Promise.resolve(gl).then(apply).catch(reject);else apply(gl);
|
|
17089
17347
|
} catch (error) {
|
|
17090
|
-
|
|
17348
|
+
reject(error);
|
|
17091
17349
|
}
|
|
17092
17350
|
};
|
|
17093
|
-
|
|
17094
|
-
|
|
17095
|
-
if (store.getState().gl) return run();
|
|
17096
|
-
|
|
17097
|
-
// Another configure is creating it. Chaining keeps them in call order
|
|
17098
|
-
if (root.ready.status === 'pending') return tracked(root.ready.then(() => run()));
|
|
17099
|
-
|
|
17100
|
-
// Only an async factory makes configure asynchronous
|
|
17101
|
-
let customRenderer;
|
|
17102
|
-
try {
|
|
17103
|
-
customRenderer = typeof glConfig === 'function' ? glConfig(defaultProps) : glConfig;
|
|
17104
|
-
} catch (error) {
|
|
17105
|
-
return fail(error);
|
|
17106
|
-
}
|
|
17107
|
-
if (!isPromiseLike(customRenderer)) return run(customRenderer);
|
|
17108
|
-
const configuring = tracked(customRenderer.then(run, fail));
|
|
17109
|
-
root.ready = configuring;
|
|
17110
|
-
return configuring;
|
|
17351
|
+
if (previous.status === 'pending') previous.then(run, reject);else run();
|
|
17352
|
+
return promise;
|
|
17111
17353
|
},
|
|
17112
17354
|
render(children) {
|
|
17113
|
-
if (
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
rootElement: canvas
|
|
17121
|
-
});
|
|
17355
|
+
if (!transitionRoot(root, {
|
|
17356
|
+
type: 'use'
|
|
17357
|
+
})) return store;
|
|
17358
|
+
|
|
17359
|
+
// The root has to be configured before it can be rendered
|
|
17360
|
+
if (!store.getState().gl && root.ready.status === 'fulfilled') this.configure();
|
|
17361
|
+
if (root.ready.status === 'rejected') throw root.ready.reason;
|
|
17122
17362
|
const commit = () => {
|
|
17123
|
-
|
|
17124
|
-
if (
|
|
17363
|
+
var _root$configuration$p;
|
|
17364
|
+
if (root.state.status !== 'open' || root.ready.status === 'rejected') return;
|
|
17365
|
+
// Wait for all queued configuration, including any queued while waiting
|
|
17366
|
+
if (root.ready.status === 'pending') {
|
|
17367
|
+
root.ready.then(commit, logRecoverableError);
|
|
17368
|
+
return;
|
|
17369
|
+
}
|
|
17370
|
+
const onCreated = (_root$configuration$p = root.configuration.previous) == null ? void 0 : _root$configuration$p.onCreated;
|
|
17371
|
+
const element = /*#__PURE__*/jsx(Provider, {
|
|
17372
|
+
store: store,
|
|
17373
|
+
children: children,
|
|
17374
|
+
onCreated: onCreated,
|
|
17375
|
+
rootElement: canvas
|
|
17376
|
+
});
|
|
17125
17377
|
if (mounted) {
|
|
17126
17378
|
reconciler.updateContainer(element, fiber, null, noop);
|
|
17127
17379
|
return;
|
|
@@ -17133,22 +17385,11 @@ function createRoot(canvas) {
|
|
|
17133
17385
|
// @ts-ignore - reconciler types are not maintained
|
|
17134
17386
|
reconciler.flushSyncWork();
|
|
17135
17387
|
};
|
|
17136
|
-
|
|
17137
|
-
// The root has to be configured before it can be rendered
|
|
17138
|
-
if (!store.getState().gl && root.ready.status === 'fulfilled') this.configure();
|
|
17139
|
-
switch (root.ready.status) {
|
|
17140
|
-
case 'pending':
|
|
17141
|
-
root.ready.then(commit, logRecoverableError);
|
|
17142
|
-
break;
|
|
17143
|
-
case 'rejected':
|
|
17144
|
-
throw root.ready.reason;
|
|
17145
|
-
default:
|
|
17146
|
-
commit();
|
|
17147
|
-
}
|
|
17388
|
+
commit();
|
|
17148
17389
|
return store;
|
|
17149
17390
|
},
|
|
17150
17391
|
unmount() {
|
|
17151
|
-
unmountComponentAtNode(canvas);
|
|
17392
|
+
if (_roots.get(canvas) === root) unmountComponentAtNode(canvas);
|
|
17152
17393
|
}
|
|
17153
17394
|
};
|
|
17154
17395
|
}
|
|
@@ -17179,147 +17420,51 @@ function Provider({
|
|
|
17179
17420
|
children: children
|
|
17180
17421
|
});
|
|
17181
17422
|
}
|
|
17423
|
+
function attempt(step) {
|
|
17424
|
+
try {
|
|
17425
|
+
return step();
|
|
17426
|
+
} catch (error) {
|
|
17427
|
+
console.warn('[R3F] Error while unmounting root; teardown may be incomplete:', error);
|
|
17428
|
+
}
|
|
17429
|
+
}
|
|
17182
17430
|
function unmountComponentAtNode(canvas, callback) {
|
|
17183
17431
|
const root = _roots.get(canvas);
|
|
17184
|
-
|
|
17432
|
+
const token = Symbol('unmount');
|
|
17433
|
+
if (!root || !transitionRoot(root, {
|
|
17434
|
+
type: 'unmount',
|
|
17435
|
+
token
|
|
17436
|
+
})) return;
|
|
17437
|
+
const teardown = () => {
|
|
17438
|
+
var _state$gl;
|
|
17439
|
+
// Configuration accepted before the unmount finishes first, so a renderer it creates is released too
|
|
17440
|
+
if (root.ready.status === 'pending') {
|
|
17441
|
+
root.ready.then(teardown, teardown);
|
|
17442
|
+
return;
|
|
17443
|
+
}
|
|
17444
|
+
// Refused when the root was used again since this unmount
|
|
17445
|
+
if (!transitionRoot(root, {
|
|
17446
|
+
type: 'dispose',
|
|
17447
|
+
token
|
|
17448
|
+
})) return;
|
|
17449
|
+
// Close the gates before invoking user cleanup code.
|
|
17450
|
+
_roots.delete(canvas);
|
|
17451
|
+
const state = root.store.getState();
|
|
17452
|
+
state.internal.active = false;
|
|
17185
17453
|
|
|
17186
|
-
|
|
17187
|
-
|
|
17454
|
+
// Release what uses the renderer before the renderer itself
|
|
17455
|
+
attempt(() => root.unwatchDpr == null ? void 0 : root.unwatchDpr());
|
|
17456
|
+
attempt(() => state.events.disconnect == null ? void 0 : state.events.disconnect());
|
|
17457
|
+
if ((_state$gl = state.gl) != null && _state$gl.xr) attempt(() => state.xr.disconnect());
|
|
17458
|
+
if (state.scene) attempt(() => dispose(state.scene));
|
|
17459
|
+
const disposal = attempt(() => releaseRenderer(state.gl, root.ownsRenderer));
|
|
17460
|
+
if (isPromiseLike(disposal)) disposal.then(() => callback == null ? void 0 : callback(canvas));else callback == null ? void 0 : callback(canvas);
|
|
17461
|
+
};
|
|
17188
17462
|
reconciler.updateContainer(null, root.fiber, null, () => {
|
|
17189
|
-
|
|
17190
|
-
|
|
17463
|
+
// A root used again since the unmount keeps its new children
|
|
17464
|
+
if (root.state.status !== 'closing' || root.state.token !== token) return;
|
|
17191
17465
|
// Effect cleanups flush before the next update
|
|
17192
|
-
reconciler.updateContainer(null, root.fiber, null,
|
|
17193
|
-
if (root.unmountClaim !== claim) return;
|
|
17194
|
-
const teardown = () => {
|
|
17195
|
-
if (root.unmountClaim !== claim) return;
|
|
17196
|
-
root.unmountClaim = null;
|
|
17197
|
-
const state = root.store.getState();
|
|
17198
|
-
state.internal.active = false;
|
|
17199
|
-
try {
|
|
17200
|
-
var _state$gl, _state$gl$renderLists, _state$gl2, _state$gl3;
|
|
17201
|
-
state.events.disconnect == null ? void 0 : state.events.disconnect();
|
|
17202
|
-
(_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();
|
|
17203
|
-
(_state$gl2 = state.gl) == null ? void 0 : _state$gl2.forceContextLoss == null ? void 0 : _state$gl2.forceContextLoss();
|
|
17204
|
-
if ((_state$gl3 = state.gl) != null && _state$gl3.xr) state.xr.disconnect();
|
|
17205
|
-
dispose(state.scene);
|
|
17206
|
-
} catch (e) {
|
|
17207
|
-
/* ... */
|
|
17208
|
-
}
|
|
17209
|
-
_roots.delete(canvas);
|
|
17210
|
-
if (callback) callback(canvas);
|
|
17211
|
-
};
|
|
17212
|
-
|
|
17213
|
-
// A renderer still being created has to exist before it can be disposed
|
|
17214
|
-
if (root.ready.status === 'pending') root.ready.then(teardown, teardown);else teardown();
|
|
17215
|
-
});
|
|
17216
|
-
});
|
|
17217
|
-
}
|
|
17218
|
-
function createPortal(children, container, state) {
|
|
17219
|
-
return /*#__PURE__*/jsx(Portal, {
|
|
17220
|
-
children: children,
|
|
17221
|
-
container: container,
|
|
17222
|
-
state: state
|
|
17223
|
-
});
|
|
17224
|
-
}
|
|
17225
|
-
function Portal({
|
|
17226
|
-
state = {},
|
|
17227
|
-
children,
|
|
17228
|
-
container
|
|
17229
|
-
}) {
|
|
17230
|
-
/** This has to be a component because it would not be able to call useThree/useStore otherwise since
|
|
17231
|
-
* if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
|
|
17232
|
-
* the "R3F hooks can only be used within the Canvas component!" warning:
|
|
17233
|
-
* <Canvas>
|
|
17234
|
-
* {createPortal(...)} */
|
|
17235
|
-
const {
|
|
17236
|
-
events,
|
|
17237
|
-
size,
|
|
17238
|
-
...rest
|
|
17239
|
-
} = state;
|
|
17240
|
-
const previousRoot = useStore();
|
|
17241
|
-
const [raycaster] = React.useState(() => new THREE.Raycaster());
|
|
17242
|
-
const [pointer] = React.useState(() => new THREE.Vector2());
|
|
17243
|
-
const inject = useMutableCallback((rootState, injectState) => {
|
|
17244
|
-
let viewport = undefined;
|
|
17245
|
-
if (injectState.camera && size) {
|
|
17246
|
-
const camera = injectState.camera;
|
|
17247
|
-
// Calculate the override viewport, if present
|
|
17248
|
-
viewport = rootState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size);
|
|
17249
|
-
// Update the portal camera, if it differs from the previous layer
|
|
17250
|
-
if (camera !== rootState.camera) updateCamera(camera, size);
|
|
17251
|
-
}
|
|
17252
|
-
return {
|
|
17253
|
-
// The intersect consists of the previous root state
|
|
17254
|
-
...rootState,
|
|
17255
|
-
...injectState,
|
|
17256
|
-
// Portals have their own scene, which forms the root, a raycaster and a pointer
|
|
17257
|
-
scene: container,
|
|
17258
|
-
raycaster,
|
|
17259
|
-
pointer,
|
|
17260
|
-
mouse: pointer,
|
|
17261
|
-
// Their previous root is the layer before it
|
|
17262
|
-
previousRoot,
|
|
17263
|
-
// Events, size and viewport can be overridden by the inject layer
|
|
17264
|
-
events: {
|
|
17265
|
-
...rootState.events,
|
|
17266
|
-
...injectState.events,
|
|
17267
|
-
...events
|
|
17268
|
-
},
|
|
17269
|
-
size: {
|
|
17270
|
-
...rootState.size,
|
|
17271
|
-
...size
|
|
17272
|
-
},
|
|
17273
|
-
viewport: {
|
|
17274
|
-
...rootState.viewport,
|
|
17275
|
-
...viewport
|
|
17276
|
-
},
|
|
17277
|
-
// Layers are allowed to override events
|
|
17278
|
-
setEvents: events => injectState.set(state => ({
|
|
17279
|
-
...state,
|
|
17280
|
-
events: {
|
|
17281
|
-
...state.events,
|
|
17282
|
-
...events
|
|
17283
|
-
}
|
|
17284
|
-
}))
|
|
17285
|
-
};
|
|
17466
|
+
reconciler.updateContainer(null, root.fiber, null, teardown);
|
|
17286
17467
|
});
|
|
17287
|
-
const usePortalStore = React.useMemo(() => {
|
|
17288
|
-
// Create a mirrored store, based on the previous root with a few overrides ...
|
|
17289
|
-
const store = createWithEqualityFn((set, get) => ({
|
|
17290
|
-
...rest,
|
|
17291
|
-
set,
|
|
17292
|
-
get
|
|
17293
|
-
}));
|
|
17294
|
-
|
|
17295
|
-
// Subscribe to previous root-state and copy changes over to the mirrored portal-state
|
|
17296
|
-
const onMutate = prev => store.setState(state => inject.current(prev, state));
|
|
17297
|
-
onMutate(previousRoot.getState());
|
|
17298
|
-
previousRoot.subscribe(onMutate);
|
|
17299
|
-
return store;
|
|
17300
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
17301
|
-
}, [previousRoot, container]);
|
|
17302
|
-
return (
|
|
17303
|
-
/*#__PURE__*/
|
|
17304
|
-
// @ts-ignore, reconciler types are not maintained
|
|
17305
|
-
jsx(Fragment, {
|
|
17306
|
-
children: reconciler.createPortal( /*#__PURE__*/jsx(context.Provider, {
|
|
17307
|
-
value: usePortalStore,
|
|
17308
|
-
children: children
|
|
17309
|
-
}), usePortalStore, null)
|
|
17310
|
-
})
|
|
17311
|
-
);
|
|
17312
|
-
}
|
|
17313
|
-
|
|
17314
|
-
/**
|
|
17315
|
-
* Force React to flush any updates inside the provided callback synchronously and immediately.
|
|
17316
|
-
* All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync).
|
|
17317
|
-
* Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without
|
|
17318
|
-
* having to revert to a non-React solution. Note: this will only flush updates within the `Canvas` root.
|
|
17319
|
-
*/
|
|
17320
|
-
function flushSync(fn) {
|
|
17321
|
-
// @ts-ignore - reconciler types are not maintained
|
|
17322
|
-
return reconciler.flushSyncFromReconciler(fn);
|
|
17323
17468
|
}
|
|
17324
17469
|
|
|
17325
17470
|
function createSubs(callback, subs) {
|
|
@@ -17474,6 +17619,141 @@ function advance(timestamp, runGlobalEffects = true, state, frame) {
|
|
|
17474
17619
|
if (runGlobalEffects) flushGlobalEffects('after', timestamp);
|
|
17475
17620
|
}
|
|
17476
17621
|
|
|
17622
|
+
function createPortal(children, container, state) {
|
|
17623
|
+
return /*#__PURE__*/jsx(Portal, {
|
|
17624
|
+
children: children,
|
|
17625
|
+
container: container,
|
|
17626
|
+
state: state
|
|
17627
|
+
});
|
|
17628
|
+
}
|
|
17629
|
+
function initializePortal(options, initialState) {
|
|
17630
|
+
const {
|
|
17631
|
+
parentStore,
|
|
17632
|
+
container
|
|
17633
|
+
} = options;
|
|
17634
|
+
const portalStore = createWithEqualityFn((set, get) => ({
|
|
17635
|
+
...initialState,
|
|
17636
|
+
set,
|
|
17637
|
+
get,
|
|
17638
|
+
// Create once with the store's setter, without retaining a state snapshot.
|
|
17639
|
+
setEvents: events => set(state => ({
|
|
17640
|
+
...state,
|
|
17641
|
+
events: {
|
|
17642
|
+
...state.events,
|
|
17643
|
+
...events
|
|
17644
|
+
}
|
|
17645
|
+
}))
|
|
17646
|
+
}));
|
|
17647
|
+
const parentStateRef = {
|
|
17648
|
+
current: parentStore.getState()
|
|
17649
|
+
};
|
|
17650
|
+
portalStore.setState(portalState => inject(parentStateRef.current, portalState, options));
|
|
17651
|
+
return {
|
|
17652
|
+
parentStore,
|
|
17653
|
+
container,
|
|
17654
|
+
portalStore,
|
|
17655
|
+
parentStateRef
|
|
17656
|
+
};
|
|
17657
|
+
}
|
|
17658
|
+
function Portal({
|
|
17659
|
+
state = {},
|
|
17660
|
+
children,
|
|
17661
|
+
container
|
|
17662
|
+
}) {
|
|
17663
|
+
const parentStore = useStore();
|
|
17664
|
+
const {
|
|
17665
|
+
events,
|
|
17666
|
+
size,
|
|
17667
|
+
...rest
|
|
17668
|
+
} = state;
|
|
17669
|
+
const [raycaster] = React.useState(() => new THREE.Raycaster());
|
|
17670
|
+
const [pointer] = React.useState(() => new THREE.Vector2());
|
|
17671
|
+
const options = {
|
|
17672
|
+
parentStore,
|
|
17673
|
+
container,
|
|
17674
|
+
raycaster,
|
|
17675
|
+
pointer,
|
|
17676
|
+
events,
|
|
17677
|
+
size
|
|
17678
|
+
};
|
|
17679
|
+
const injectRef = useMutableCallback((parentState, portalState) => inject(parentState, portalState, options));
|
|
17680
|
+
|
|
17681
|
+
// Initialize during render. Keep the store in state so Activity can preserve it.
|
|
17682
|
+
const [portal, setPortal] = React.useState(() => initializePortal(options, rest));
|
|
17683
|
+
if (portal.parentStore !== parentStore || portal.container !== container) {
|
|
17684
|
+
setPortal(initializePortal(options, rest));
|
|
17685
|
+
}
|
|
17686
|
+
const {
|
|
17687
|
+
portalStore,
|
|
17688
|
+
parentStateRef
|
|
17689
|
+
} = portal;
|
|
17690
|
+
|
|
17691
|
+
// Subscribe after commit and update the portal on each parent change.
|
|
17692
|
+
useIsomorphicLayoutEffect(() => {
|
|
17693
|
+
const update = parentState => {
|
|
17694
|
+
parentStateRef.current = parentState;
|
|
17695
|
+
portalStore.setState(portalState => injectRef.current(parentState, portalState));
|
|
17696
|
+
};
|
|
17697
|
+
const unsubscribe = parentStore.subscribe(update);
|
|
17698
|
+
|
|
17699
|
+
// Catch up on changes made before subscribing or while Activity was hidden.
|
|
17700
|
+
const parentState = parentStore.getState();
|
|
17701
|
+
if (parentState !== parentStateRef.current) update(parentState);
|
|
17702
|
+
|
|
17703
|
+
// Clean up when hidden, unmounted or switching stores.
|
|
17704
|
+
return unsubscribe;
|
|
17705
|
+
}, [parentStore, portalStore, parentStateRef]);
|
|
17706
|
+
return /*#__PURE__*/jsx(Fragment, {
|
|
17707
|
+
children: reconciler.createPortal( /*#__PURE__*/jsx(context.Provider, {
|
|
17708
|
+
value: portalStore,
|
|
17709
|
+
children: children
|
|
17710
|
+
}), portalStore, null)
|
|
17711
|
+
});
|
|
17712
|
+
}
|
|
17713
|
+
function inject(parentState, portalState, options) {
|
|
17714
|
+
const {
|
|
17715
|
+
parentStore,
|
|
17716
|
+
container,
|
|
17717
|
+
raycaster,
|
|
17718
|
+
pointer,
|
|
17719
|
+
events,
|
|
17720
|
+
size
|
|
17721
|
+
} = options;
|
|
17722
|
+
let viewport = undefined;
|
|
17723
|
+
if (portalState.camera && size) {
|
|
17724
|
+
const camera = portalState.camera;
|
|
17725
|
+
// Calculate the override viewport, if present
|
|
17726
|
+
viewport = parentState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size);
|
|
17727
|
+
// Update the portal camera, if it differs from the previous layer
|
|
17728
|
+
if (camera !== parentState.camera) updateCamera(camera, size);
|
|
17729
|
+
}
|
|
17730
|
+
return {
|
|
17731
|
+
// Inherit parent values and preserve the portal's own state.
|
|
17732
|
+
...parentState,
|
|
17733
|
+
...portalState,
|
|
17734
|
+
// Portals have their own scene, raycaster and pointer.
|
|
17735
|
+
scene: container,
|
|
17736
|
+
raycaster,
|
|
17737
|
+
pointer,
|
|
17738
|
+
mouse: pointer,
|
|
17739
|
+
previousRoot: parentStore,
|
|
17740
|
+
// Apply the portal's event, size and viewport overrides.
|
|
17741
|
+
events: {
|
|
17742
|
+
...parentState.events,
|
|
17743
|
+
...portalState.events,
|
|
17744
|
+
...events
|
|
17745
|
+
},
|
|
17746
|
+
size: {
|
|
17747
|
+
...parentState.size,
|
|
17748
|
+
...size
|
|
17749
|
+
},
|
|
17750
|
+
viewport: {
|
|
17751
|
+
...parentState.viewport,
|
|
17752
|
+
...viewport
|
|
17753
|
+
}
|
|
17754
|
+
};
|
|
17755
|
+
}
|
|
17756
|
+
|
|
17477
17757
|
const DOM_EVENTS = {
|
|
17478
17758
|
onClick: ['click', false],
|
|
17479
17759
|
onContextMenu: ['contextmenu', false],
|
|
@@ -17560,4 +17840,4 @@ function createPointerEvents(store) {
|
|
|
17560
17840
|
};
|
|
17561
17841
|
}
|
|
17562
17842
|
|
|
17563
|
-
export { useInstanceHandle as A, Block as B, useStore as C, useThree as D, ErrorBoundary as E, useFrame as F, useGraph as G, useLoader as H, _roots as _, useMutableCallback as a, useGate as b, useIsomorphicLayoutEffect as c, createRoot as d, extend as e,
|
|
17843
|
+
export { useInstanceHandle as A, Block as B, useStore as C, useThree as D, ErrorBoundary as E, useFrame as F, useGraph as G, useLoader as H, _roots as _, useMutableCallback as a, useGate as b, useIsomorphicLayoutEffect as c, createRoot as d, extend as e, createPointerEvents as f, createEvents as g, flushGlobalEffects as h, isRef as i, addEffect as j, addAfterEffect as k, addTail as l, invalidate as m, advance as n, flushSync as o, unmountComponentAtNode as p, createPortal as q, reconciler as r, context as s, threeTypes as t, useBridge as u, applyProps as v, getRootState as w, dispose as x, act as y, buildGraph as z };
|