@react-three/fiber 9.8.0 → 9.8.1

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,11 +1,11 @@
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';
6
7
  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';
8
+ import { useFiber, useContextBridge, traverseFiber, useActivityBridge as useActivityBridge$1 } from 'its-fine';
9
9
 
10
10
  var threeTypes = /*#__PURE__*/Object.freeze({
11
11
  __proto__: null
@@ -70,23 +70,29 @@ function useGate() {
70
70
  }) : null;
71
71
  return [gate, waitFor];
72
72
  }
73
+
74
+ // Activity is optional on React 19.0/19.1. A computed key also supports strict ESM bundlers.
75
+ const useActivityBridge = React['Activity' + ''] ? useActivityBridge$1 : () => React.Fragment;
73
76
  /**
74
- * Bridges renderer Context and StrictMode from a primary renderer.
77
+ * Bridges Context, StrictMode, and Activity visibility from a primary renderer.
75
78
  */
76
79
  function useBridge() {
77
80
  const fiber = useFiber();
78
81
  const ContextBridge = useContextBridge();
82
+ const ActivityBridge = useActivityBridge();
79
83
  return React.useMemo(() => ({
80
84
  children
81
85
  }) => {
82
86
  const strict = !!traverseFiber(fiber, true, node => node.type === React.StrictMode);
83
87
  const Root = strict ? React.StrictMode : React.Fragment;
84
88
  return /*#__PURE__*/jsx(Root, {
85
- children: /*#__PURE__*/jsx(ContextBridge, {
86
- children: children
89
+ children: /*#__PURE__*/jsx(ActivityBridge, {
90
+ children: /*#__PURE__*/jsx(ContextBridge, {
91
+ children: children
92
+ })
87
93
  })
88
94
  });
89
- }, [fiber, ContextBridge]);
95
+ }, [fiber, ContextBridge, ActivityBridge]);
90
96
  }
91
97
  function Block({
92
98
  set
@@ -232,6 +238,7 @@ function prepare(target, root, type, props) {
232
238
  object,
233
239
  eventCount: 0,
234
240
  handlers: {},
241
+ previousVisible: undefined,
235
242
  isHidden: false
236
243
  };
237
244
  if (object) object.__r3f = instance;
@@ -415,6 +422,12 @@ function applyProps(object, props) {
415
422
  // Ignore setting undefined props
416
423
  // https://github.com/pmndrs/react-three-fiber/issues/274
417
424
  if (value === undefined) continue;
425
+
426
+ // Update the visibility to restore without revealing a hidden instance.
427
+ if (prop === 'visible' && instance != null && instance.isHidden && instance.previousVisible !== undefined) {
428
+ instance.previousVisible = value;
429
+ continue;
430
+ }
418
431
  let {
419
432
  root,
420
433
  key,
@@ -1339,9 +1352,326 @@ useLoader.clear = function (loader, input) {
1339
1352
  e = 2,
1340
1353
  i$1 = 268435456;
1341
1354
 
1355
+ // Shim for OffscreenCanvas since it was removed from DOM types
1356
+ // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54988
1357
+
1358
+ /** What a root has applied, so its next configuration applies only what changed */
1359
+
1360
+ const shallow = {
1361
+ objects: 'shallow'
1362
+ };
1363
+
1364
+ /** The renderer to configure, or a promise for one from an async factory */
1365
+ function createRenderer(canvas, gl) {
1366
+ const defaults = {
1367
+ canvas,
1368
+ powerPreference: 'high-performance',
1369
+ antialias: true,
1370
+ alpha: true
1371
+ };
1372
+ if (typeof gl === 'function') return gl(defaults);
1373
+ if (isRenderer(gl)) return gl;
1374
+ return new THREE.WebGLRenderer({
1375
+ ...defaults,
1376
+ ...gl
1377
+ });
1378
+ }
1379
+ function computeInitialSize(canvas, size) {
1380
+ if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1381
+ const {
1382
+ width,
1383
+ height,
1384
+ top,
1385
+ left
1386
+ } = canvas.parentElement.getBoundingClientRect();
1387
+ return {
1388
+ width,
1389
+ height,
1390
+ top,
1391
+ left
1392
+ };
1393
+ } else if (!size && typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
1394
+ return {
1395
+ width: canvas.width,
1396
+ height: canvas.height,
1397
+ top: 0,
1398
+ left: 0
1399
+ };
1400
+ }
1401
+ return {
1402
+ width: 0,
1403
+ height: 0,
1404
+ top: 0,
1405
+ left: 0,
1406
+ ...size
1407
+ };
1408
+ }
1409
+
1410
+ /**
1411
+ * Applies the inputs that changed since the last configuration applied in full, to a root whose
1412
+ * renderer is installed. A runtime change, such as setFrameloop or an edit to gl.shadowMap, lasts
1413
+ * until its input changes. The pixel ratio instead follows the device on every call. Objects and
1414
+ * arrays compare shallowly, so an equal inline value is unchanged.
1415
+ */
1416
+ function applyRootConfiguration({
1417
+ store,
1418
+ configuration
1419
+ }, canvas, props) {
1420
+ const {
1421
+ gl: glConfig,
1422
+ scene: sceneOptions,
1423
+ camera: cameraOptions,
1424
+ shadows = false,
1425
+ linear = false,
1426
+ flat = false,
1427
+ legacy = false,
1428
+ frameloop = 'always',
1429
+ dpr = [1, 2],
1430
+ performance,
1431
+ onPointerMissed
1432
+ } = props;
1433
+ const size = computeInitialSize(canvas, props.size);
1434
+ const next = {
1435
+ ...props,
1436
+ shadows,
1437
+ linear,
1438
+ flat,
1439
+ legacy,
1440
+ frameloop,
1441
+ size
1442
+ };
1443
+ const state = store.getState();
1444
+ const gl = state.gl;
1445
+ const last = configuration.previous;
1446
+ const changed = key => !last || !is.equ(next[key], last[key], shallow);
1447
+ // A failure can leave inputs partly applied, so the next configuration applies all of them
1448
+ configuration.previous = undefined;
1449
+
1450
+ // Set up raycaster (one time only!)
1451
+ let raycaster = state.raycaster;
1452
+ if (!raycaster) state.set({
1453
+ raycaster: raycaster = new THREE.Raycaster()
1454
+ });
1455
+ if (changed('raycaster')) {
1456
+ var _props$raycaster;
1457
+ const {
1458
+ params,
1459
+ ...options
1460
+ } = (_props$raycaster = props.raycaster) != null ? _props$raycaster : {};
1461
+ applyProps(raycaster, {
1462
+ ...options,
1463
+ params: {
1464
+ ...raycaster.params,
1465
+ ...params
1466
+ }
1467
+ });
1468
+ }
1469
+
1470
+ // Create default camera, don't overwrite any user-set state
1471
+ if (!state.camera || state.camera === configuration.camera && changed('camera')) {
1472
+ configuration.camera = cameraOptions;
1473
+ const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
1474
+ const camera = isCamera ? cameraOptions : props.orthographic ? new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE.PerspectiveCamera(75, 0, 0.1, 1000);
1475
+ if (!isCamera) {
1476
+ camera.position.z = 5;
1477
+ if (cameraOptions) {
1478
+ applyProps(camera, cameraOptions);
1479
+ // Preserve user-defined frustum if possible
1480
+ // https://github.com/pmndrs/react-three-fiber/issues/3160
1481
+ if (!camera.manual) {
1482
+ if ('aspect' in cameraOptions || 'left' in cameraOptions || 'right' in cameraOptions || 'bottom' in cameraOptions || 'top' in cameraOptions) {
1483
+ camera.manual = true;
1484
+ camera.updateProjectionMatrix();
1485
+ }
1486
+ }
1487
+ }
1488
+ // Always look at center by default
1489
+ if (!state.camera && !(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
1490
+ }
1491
+ state.set({
1492
+ camera
1493
+ });
1494
+
1495
+ // Configure raycaster
1496
+ // https://github.com/pmndrs/react-xr/issues/300
1497
+ raycaster.camera = camera;
1498
+ }
1499
+
1500
+ // Set up scene (one time only!)
1501
+ if (!state.scene) {
1502
+ let scene;
1503
+ if (sceneOptions != null && sceneOptions.isScene) {
1504
+ scene = sceneOptions;
1505
+ prepare(scene, store, '', {});
1506
+ } else {
1507
+ scene = new THREE.Scene();
1508
+ prepare(scene, store, '', {});
1509
+ if (sceneOptions) applyProps(scene, sceneOptions);
1510
+ }
1511
+ state.set({
1512
+ scene
1513
+ });
1514
+ }
1515
+
1516
+ // Store events internally
1517
+ if (props.events && !state.events.handlers) state.set({
1518
+ events: props.events(store)
1519
+ });
1520
+ if (changed('size')) state.setSize(size.width, size.height, size.top, size.left);
1521
+ // The ratio follows window.devicePixelRatio, which changes with browser zoom or another display
1522
+ if (state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
1523
+ if (changed('frameloop')) state.setFrameloop(frameloop);
1524
+ if (changed('onPointerMissed')) state.set({
1525
+ onPointerMissed
1526
+ });
1527
+ // Performance options merge, so removing them keeps the current settings
1528
+ if (performance && changed('performance')) {
1529
+ state.set(state => ({
1530
+ performance: {
1531
+ ...state.performance,
1532
+ ...performance
1533
+ }
1534
+ }));
1535
+ }
1536
+
1537
+ // Set up XR (one time only!)
1538
+ if (!state.xr) {
1539
+ var _gl$xr;
1540
+ // Handle frame behavior in WebXR
1541
+ const handleXRFrame = (timestamp, frame) => {
1542
+ const state = store.getState();
1543
+ if (state.frameloop === 'never') return;
1544
+ advance(timestamp, true, state, frame);
1545
+ };
1546
+
1547
+ // Toggle render switching on session
1548
+ const handleSessionChange = () => {
1549
+ const state = store.getState();
1550
+ state.gl.xr.enabled = state.gl.xr.isPresenting;
1551
+ state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
1552
+ if (!state.gl.xr.isPresenting) invalidate(state);
1553
+ };
1554
+
1555
+ // WebXR session manager
1556
+ const xr = {
1557
+ connect() {
1558
+ const gl = store.getState().gl;
1559
+ gl.xr.addEventListener('sessionstart', handleSessionChange);
1560
+ gl.xr.addEventListener('sessionend', handleSessionChange);
1561
+ },
1562
+ disconnect() {
1563
+ const gl = store.getState().gl;
1564
+ gl.xr.removeEventListener('sessionstart', handleSessionChange);
1565
+ gl.xr.removeEventListener('sessionend', handleSessionChange);
1566
+ }
1567
+ };
1568
+
1569
+ // Subscribe to WebXR session events
1570
+ if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
1571
+ state.set({
1572
+ xr
1573
+ });
1574
+ }
1575
+
1576
+ // Set shadowmap
1577
+ if (gl.shadowMap && changed('shadows')) {
1578
+ const oldEnabled = gl.shadowMap.enabled;
1579
+ const oldType = gl.shadowMap.type;
1580
+ gl.shadowMap.enabled = !!shadows;
1581
+ if (is.boo(shadows)) {
1582
+ gl.shadowMap.type = THREE.PCFSoftShadowMap;
1583
+ } else if (is.str(shadows)) {
1584
+ var _types$shadows;
1585
+ const types = {
1586
+ basic: THREE.BasicShadowMap,
1587
+ percentage: THREE.PCFShadowMap,
1588
+ soft: THREE.PCFSoftShadowMap,
1589
+ variance: THREE.VSMShadowMap
1590
+ };
1591
+ gl.shadowMap.type = (_types$shadows = types[shadows]) != null ? _types$shadows : THREE.PCFSoftShadowMap;
1592
+ } else if (is.obj(shadows)) {
1593
+ Object.assign(gl.shadowMap, shadows);
1594
+ }
1595
+ if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
1596
+ }
1597
+
1598
+ // Explicit renderer options take precedence over the color shorthands
1599
+ const glProps = glConfig && !is.fun(glConfig) && !isRenderer(glConfig) ? glConfig : undefined;
1600
+ if (changed('legacy')) {
1601
+ THREE.ColorManagement.enabled = !legacy;
1602
+ state.set({
1603
+ legacy
1604
+ });
1605
+ }
1606
+ if (changed('linear')) {
1607
+ if ((glProps == null ? void 0 : glProps.outputColorSpace) === undefined) {
1608
+ gl.outputColorSpace = linear ? THREE.LinearSRGBColorSpace : THREE.SRGBColorSpace;
1609
+ }
1610
+ state.set({
1611
+ linear
1612
+ });
1613
+ }
1614
+ if (changed('flat')) {
1615
+ if ((glProps == null ? void 0 : glProps.toneMapping) === undefined) gl.toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;
1616
+ state.set({
1617
+ flat
1618
+ });
1619
+ }
1620
+
1621
+ // Set gl props
1622
+ if (glProps && changed('gl')) applyProps(gl, glProps);
1623
+ configuration.previous = next;
1624
+ }
1625
+
1626
+ const isPromiseLike = value => typeof (value == null ? void 0 : value.then) === 'function';
1627
+
1628
+ /** A promise tagged with its state, the protocol React's `use` reads */
1629
+
1630
+ const fulfilled = value => Object.assign(Promise.resolve(value), {
1631
+ status: 'fulfilled',
1632
+ value
1633
+ });
1634
+ const rejected = reason => {
1635
+ const promise = Promise.reject(reason);
1636
+ promise.catch(() => {}); // the reason is read from `status`
1637
+ return Object.assign(promise, {
1638
+ status: 'rejected',
1639
+ reason
1640
+ });
1641
+ };
1642
+
1643
+ /** A pending promise, settled once from outside. Its `status` updates the moment it settles */
1644
+ const deferred = () => {
1645
+ let resolve;
1646
+ let reject;
1647
+ const promise = new Promise((yes, no) => {
1648
+ resolve = value => {
1649
+ Object.assign(promise, {
1650
+ status: 'fulfilled',
1651
+ value
1652
+ });
1653
+ yes(value);
1654
+ };
1655
+ reject = reason => {
1656
+ Object.assign(promise, {
1657
+ status: 'rejected',
1658
+ reason
1659
+ });
1660
+ no(reason);
1661
+ };
1662
+ });
1663
+ promise.status = 'pending';
1664
+ promise.catch(() => {}); // the reason is read from `status`
1665
+ return {
1666
+ promise,
1667
+ resolve,
1668
+ reject
1669
+ };
1670
+ };
1671
+
1342
1672
  var packageData = {
1343
1673
  name: "@react-three/fiber",
1344
- version: "9.8.0",
1674
+ version: "9.8.1",
1345
1675
  description: "A React renderer for Threejs",
1346
1676
  keywords: [
1347
1677
  "react",
@@ -1392,7 +1722,7 @@ var packageData = {
1392
1722
  "@types/webxr": "*",
1393
1723
  "base64-js": "^1.5.1",
1394
1724
  buffer: "^6.0.3",
1395
- "its-fine": "^2.0.0",
1725
+ "its-fine": "^2.1.1",
1396
1726
  "react-use-measure": "^2.1.7",
1397
1727
  scheduler: "^0.28.0",
1398
1728
  "suspend-react": "^0.1.3",
@@ -16170,6 +16500,7 @@ function hideInstance(instance) {
16170
16500
  if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
16171
16501
  detach(instance.parent, instance);
16172
16502
  } else if (isObject3D(instance.object)) {
16503
+ instance.previousVisible = instance.object.visible;
16173
16504
  instance.object.visible = false;
16174
16505
  }
16175
16506
  instance.isHidden = true;
@@ -16181,9 +16512,11 @@ function unhideInstance(instance) {
16181
16512
  var _instance$parent2;
16182
16513
  if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
16183
16514
  attach(instance.parent, instance);
16184
- } else if (isObject3D(instance.object) && instance.props.visible !== false) {
16185
- instance.object.visible = true;
16515
+ } else if (isObject3D(instance.object)) {
16516
+ var _instance$previousVis;
16517
+ instance.object.visible = (_instance$previousVis = instance.previousVisible) != null ? _instance$previousVis : true;
16186
16518
  }
16519
+ instance.previousVisible = undefined;
16187
16520
  instance.isHidden = false;
16188
16521
  invalidateInstance(instance);
16189
16522
  }
@@ -16728,76 +17061,49 @@ const reconciler = /* @__PURE__ */createReconciler({
16728
17061
  getSuspendedCommitReason: (_state, _rootContainer) => null
16729
17062
  });
16730
17063
 
16731
- const isPromiseLike = value => typeof (value == null ? void 0 : value.then) === 'function';
16732
-
16733
- /** A promise tagged with its state, the protocol React's `use` reads */
16734
-
16735
- const fulfilled = value => Object.assign(Promise.resolve(value), {
16736
- status: 'fulfilled',
16737
- value
16738
- });
16739
- const rejected = reason => {
16740
- const promise = Promise.reject(reason);
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
- };
17064
+ /**
17065
+ * Force React to flush any updates inside the provided callback synchronously and immediately.
17066
+ * All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync).
17067
+ * Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without
17068
+ * having to revert to a non-React solution. Note: this will only flush updates within the `Canvas` root.
17069
+ */
17070
+ function flushSync(fn) {
17071
+ // @ts-ignore - reconciler types are not maintained
17072
+ return reconciler.flushSyncFromReconciler(fn);
17073
+ }
16766
17074
 
16767
17075
  const _roots = new Map();
16768
- const shallowLoose = {
16769
- objects: 'shallow',
16770
- strict: false
16771
- };
16772
- function computeInitialSize(canvas, size) {
16773
- if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
16774
- const {
16775
- width,
16776
- height,
16777
- top,
16778
- left
16779
- } = canvas.parentElement.getBoundingClientRect();
16780
- return {
16781
- width,
16782
- height,
16783
- top,
16784
- left
16785
- };
16786
- } else if (!size && typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
16787
- return {
16788
- width: canvas.width,
16789
- height: canvas.height,
16790
- top: 0,
16791
- left: 0
16792
- };
17076
+ /**
17077
+ * open -> closing -> disposed
17078
+ * | use
17079
+ * v
17080
+ * open
17081
+ *
17082
+ * Only closing can be cancelled. Each unmount carries a token, so only the latest one can dispose.
17083
+ */
17084
+ function transitionRoot(root, event) {
17085
+ const state = root.state;
17086
+ switch (event.type) {
17087
+ case 'use':
17088
+ if (state.status === 'disposed') return false;
17089
+ root.state = {
17090
+ status: 'open'
17091
+ };
17092
+ return true;
17093
+ case 'unmount':
17094
+ if (state.status === 'disposed') return false;
17095
+ root.state = {
17096
+ status: 'closing',
17097
+ token: event.token
17098
+ };
17099
+ return true;
17100
+ case 'dispose':
17101
+ if (state.status !== 'closing' || state.token !== event.token) return false;
17102
+ root.state = {
17103
+ status: 'disposed'
17104
+ };
17105
+ return true;
16793
17106
  }
16794
- return {
16795
- width: 0,
16796
- height: 0,
16797
- top: 0,
16798
- left: 0,
16799
- ...size
16800
- };
16801
17107
  }
16802
17108
  function createRoot(canvas) {
16803
17109
  // Check against mistaken use of createRoot
@@ -16842,286 +17148,84 @@ function createRoot(canvas) {
16842
17148
  const root = prevRoot || {
16843
17149
  fiber,
16844
17150
  store,
16845
- unmountClaim: null,
16846
- ready: fulfilled(undefined)
17151
+ state: {
17152
+ status: 'open'
17153
+ },
17154
+ ready: fulfilled(undefined),
17155
+ configuration: {},
17156
+ ownsRenderer: false
16847
17157
  };
16848
17158
  if (!prevRoot) _roots.set(canvas, root);
16849
-
16850
- // Locals
16851
- let onCreated;
16852
- let lastCamera;
16853
17159
  let mounted = false;
16854
17160
  return {
16855
17161
  get ready() {
16856
17162
  return root.ready;
16857
17163
  },
16858
17164
  configure(props = {}) {
16859
- root.unmountClaim = null;
16860
- const {
16861
- gl: glConfig,
16862
- size: propsSize,
16863
- scene: sceneOptions,
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
- };
17165
+ if (!transitionRoot(root, {
17166
+ type: 'use'
17167
+ })) {
17168
+ return rejected(new Error('R3F: Cannot configure a root after disposal has started.'));
17169
+ }
16884
17170
 
16885
- // Synchronous, and needs the renderer to exist
16886
- const apply = customRenderer => {
17171
+ // Configuration applies in call order. Publishing first queues calls made while this one runs
17172
+ const previous = root.ready;
17173
+ const {
17174
+ promise,
17175
+ resolve,
17176
+ reject
17177
+ } = deferred();
17178
+ root.ready = promise;
17179
+ const apply = gl => {
16887
17180
  const state = store.getState();
16888
-
16889
17181
  // Set up renderer (one time only!)
16890
- const first = !state.gl;
16891
- let gl = state.gl;
16892
- if (first) {
16893
- gl = isRenderer(customRenderer) ? customRenderer : new THREE.WebGLRenderer({
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
- }
17182
+ if (!state.gl) {
17183
+ // R3F owns what it builds, a factory's result included, since it calls the factory
17184
+ // once per root. A renderer instance belongs to the caller
17185
+ root.ownsRenderer = !isRenderer(props.gl);
16964
17186
  state.set({
16965
- scene
17187
+ gl: gl
16966
17188
  });
16967
17189
  }
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();
17028
- state.set({
17029
- xr
17030
- });
17031
- }
17032
-
17033
- // Set shadowmap
17034
- if (gl.shadowMap) {
17035
- const oldEnabled = gl.shadowMap.enabled;
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;
17190
+ applyRootConfiguration(root, canvas, props);
17191
+ resolve(this);
17083
17192
  };
17084
- const run = customRenderer => {
17193
+ const run = () => {
17085
17194
  try {
17086
- apply(customRenderer);
17087
- root.ready = fulfilled(undefined);
17088
- return fulfilled(this);
17195
+ var _store$getState$gl;
17196
+ const gl = (_store$getState$gl = store.getState().gl) != null ? _store$getState$gl : createRenderer(canvas, props.gl);
17197
+ // Only an async renderer factory makes configuration asynchronous
17198
+ if (isPromiseLike(gl)) Promise.resolve(gl).then(apply).catch(reject);else apply(gl);
17089
17199
  } catch (error) {
17090
- return fail(error);
17200
+ reject(error);
17091
17201
  }
17092
17202
  };
17093
-
17094
- // The renderer exists, so applying props is synchronous
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;
17203
+ if (previous.status === 'pending') previous.then(run, reject);else run();
17204
+ return promise;
17111
17205
  },
17112
17206
  render(children) {
17113
- if (_roots.get(canvas) !== root) return store;
17114
- // Using the root cancels a teardown waiting on React
17115
- root.unmountClaim = null;
17116
- const element = /*#__PURE__*/jsx(Provider, {
17117
- store: store,
17118
- children: children,
17119
- onCreated: onCreated,
17120
- rootElement: canvas
17121
- });
17207
+ if (!transitionRoot(root, {
17208
+ type: 'use'
17209
+ })) return store;
17210
+
17211
+ // The root has to be configured before it can be rendered
17212
+ if (!store.getState().gl && root.ready.status === 'fulfilled') this.configure();
17213
+ if (root.ready.status === 'rejected') throw root.ready.reason;
17122
17214
  const commit = () => {
17123
- // The root may have been unmounted or claimed while we waited
17124
- if (_roots.get(canvas) !== root || root.unmountClaim) return;
17215
+ var _root$configuration$p;
17216
+ if (root.state.status !== 'open' || root.ready.status === 'rejected') return;
17217
+ // Wait for all queued configuration, including any queued while waiting
17218
+ if (root.ready.status === 'pending') {
17219
+ root.ready.then(commit, logRecoverableError);
17220
+ return;
17221
+ }
17222
+ const onCreated = (_root$configuration$p = root.configuration.previous) == null ? void 0 : _root$configuration$p.onCreated;
17223
+ const element = /*#__PURE__*/jsx(Provider, {
17224
+ store: store,
17225
+ children: children,
17226
+ onCreated: onCreated,
17227
+ rootElement: canvas
17228
+ });
17125
17229
  if (mounted) {
17126
17230
  reconciler.updateContainer(element, fiber, null, noop);
17127
17231
  return;
@@ -17133,22 +17237,11 @@ function createRoot(canvas) {
17133
17237
  // @ts-ignore - reconciler types are not maintained
17134
17238
  reconciler.flushSyncWork();
17135
17239
  };
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
- }
17240
+ commit();
17148
17241
  return store;
17149
17242
  },
17150
17243
  unmount() {
17151
- unmountComponentAtNode(canvas);
17244
+ if (_roots.get(canvas) === root) unmountComponentAtNode(canvas);
17152
17245
  }
17153
17246
  };
17154
17247
  }
@@ -17179,147 +17272,81 @@ function Provider({
17179
17272
  children: children
17180
17273
  });
17181
17274
  }
17182
- function unmountComponentAtNode(canvas, callback) {
17183
- const root = _roots.get(canvas);
17184
- if (!root) return;
17185
-
17186
- // Cleared by configure and render, which cancels the teardown
17187
- const claim = root.unmountClaim = Symbol('unmount');
17188
- reconciler.updateContainer(null, root.fiber, null, () => {
17189
- if (root.unmountClaim !== claim) return;
17190
-
17191
- // 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
17275
 
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
- });
17276
+ /**
17277
+ * Frees a renderer R3F built. WebGLRenderer.dispose() releases programs and caches but keeps its
17278
+ * context until garbage collection, and browsers cap live WebGL contexts, so the context is lost
17279
+ * after it. A WebGPURenderer (from a factory) releases its device and context inside dispose().
17280
+ */
17281
+ function disposeRenderer(gl) {
17282
+ // Avoid starting initialization through three's dispose(). A factory must await init()
17283
+ // before returning its renderer so readiness includes initialization and teardown can wait.
17284
+ if ((gl.hasInitialized == null ? void 0 : gl.hasInitialized()) === false) return;
17285
+ const disposed = attempt(() => {
17286
+ var _gl$renderLists;
17287
+ return gl.dispose ? gl.dispose() : (_gl$renderLists = gl.renderLists) == null ? void 0 : _gl$renderLists.dispose == null ? void 0 : _gl$renderLists.dispose();
17216
17288
  });
17289
+ // WebGPURenderer.dispose() is async from three r186
17290
+ if (isPromiseLike(disposed)) {
17291
+ return Promise.resolve(disposed).catch(error => console.warn('[R3F] Error disposing renderer', error)).then(() => attempt(() => gl.forceContextLoss == null ? void 0 : gl.forceContextLoss()));
17292
+ }
17293
+ attempt(() => gl.forceContextLoss == null ? void 0 : gl.forceContextLoss());
17217
17294
  }
17218
- function createPortal(children, container, state) {
17219
- return /*#__PURE__*/jsx(Portal, {
17220
- children: children,
17221
- container: container,
17222
- state: state
17223
- });
17295
+ function attempt(step) {
17296
+ try {
17297
+ return step();
17298
+ } catch (error) {
17299
+ console.warn('[R3F] Error while unmounting root; teardown may be incomplete:', error);
17300
+ }
17224
17301
  }
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);
17302
+ function unmountComponentAtNode(canvas, callback) {
17303
+ const root = _roots.get(canvas);
17304
+ const token = Symbol('unmount');
17305
+ if (!root || !transitionRoot(root, {
17306
+ type: 'unmount',
17307
+ token
17308
+ })) return;
17309
+ const teardown = () => {
17310
+ var _state$gl;
17311
+ // Configuration accepted before the unmount finishes first, so a renderer it creates is released too
17312
+ if (root.ready.status === 'pending') {
17313
+ root.ready.then(teardown, teardown);
17314
+ return;
17251
17315
  }
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
- };
17286
- });
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
- }
17316
+ // Refused when the root was used again since this unmount
17317
+ if (!transitionRoot(root, {
17318
+ type: 'dispose',
17319
+ token
17320
+ })) return;
17321
+ // Close the gates before invoking user cleanup code.
17322
+ _roots.delete(canvas);
17323
+ const state = root.store.getState();
17324
+ state.internal.active = false;
17313
17325
 
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);
17326
+ // Release what uses the renderer before the renderer itself
17327
+ attempt(() => state.events.disconnect == null ? void 0 : state.events.disconnect());
17328
+ if ((_state$gl = state.gl) != null && _state$gl.xr) attempt(() => state.xr.disconnect());
17329
+ if (state.scene) attempt(() => dispose(state.scene));
17330
+ const gl = state.gl;
17331
+ let disposal = undefined;
17332
+ if (gl && root.ownsRenderer) {
17333
+ disposal = attempt(() => disposeRenderer(gl));
17334
+ } else if (gl) {
17335
+ // A renderer passed in keeps its 9.x teardown: its context is lost but it is not disposed
17336
+ attempt(() => {
17337
+ var _gl$renderLists2;
17338
+ return (_gl$renderLists2 = gl.renderLists) == null ? void 0 : _gl$renderLists2.dispose == null ? void 0 : _gl$renderLists2.dispose();
17339
+ });
17340
+ attempt(() => gl.forceContextLoss == null ? void 0 : gl.forceContextLoss());
17341
+ }
17342
+ if (isPromiseLike(disposal)) disposal.then(() => callback == null ? void 0 : callback(canvas));else callback == null ? void 0 : callback(canvas);
17343
+ };
17344
+ reconciler.updateContainer(null, root.fiber, null, () => {
17345
+ // A root used again since the unmount keeps its new children
17346
+ if (root.state.status !== 'closing' || root.state.token !== token) return;
17347
+ // Effect cleanups flush before the next update
17348
+ reconciler.updateContainer(null, root.fiber, null, teardown);
17349
+ });
17323
17350
  }
17324
17351
 
17325
17352
  function createSubs(callback, subs) {
@@ -17474,6 +17501,102 @@ function advance(timestamp, runGlobalEffects = true, state, frame) {
17474
17501
  if (runGlobalEffects) flushGlobalEffects('after', timestamp);
17475
17502
  }
17476
17503
 
17504
+ function createPortal(children, container, state) {
17505
+ return /*#__PURE__*/jsx(Portal, {
17506
+ children: children,
17507
+ container: container,
17508
+ state: state
17509
+ });
17510
+ }
17511
+ function Portal({
17512
+ state = {},
17513
+ children,
17514
+ container
17515
+ }) {
17516
+ /** This has to be a component because it would not be able to call useThree/useStore otherwise since
17517
+ * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
17518
+ * the "R3F hooks can only be used within the Canvas component!" warning:
17519
+ * <Canvas>
17520
+ * {createPortal(...)} */
17521
+ const {
17522
+ events,
17523
+ size,
17524
+ ...rest
17525
+ } = state;
17526
+ const previousRoot = useStore();
17527
+ const [raycaster] = React.useState(() => new THREE.Raycaster());
17528
+ const [pointer] = React.useState(() => new THREE.Vector2());
17529
+ const inject = useMutableCallback((rootState, injectState) => {
17530
+ let viewport = undefined;
17531
+ if (injectState.camera && size) {
17532
+ const camera = injectState.camera;
17533
+ // Calculate the override viewport, if present
17534
+ viewport = rootState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size);
17535
+ // Update the portal camera, if it differs from the previous layer
17536
+ if (camera !== rootState.camera) updateCamera(camera, size);
17537
+ }
17538
+ return {
17539
+ // The intersect consists of the previous root state
17540
+ ...rootState,
17541
+ ...injectState,
17542
+ // Portals have their own scene, which forms the root, a raycaster and a pointer
17543
+ scene: container,
17544
+ raycaster,
17545
+ pointer,
17546
+ mouse: pointer,
17547
+ // Their previous root is the layer before it
17548
+ previousRoot,
17549
+ // Events, size and viewport can be overridden by the inject layer
17550
+ events: {
17551
+ ...rootState.events,
17552
+ ...injectState.events,
17553
+ ...events
17554
+ },
17555
+ size: {
17556
+ ...rootState.size,
17557
+ ...size
17558
+ },
17559
+ viewport: {
17560
+ ...rootState.viewport,
17561
+ ...viewport
17562
+ },
17563
+ // Layers are allowed to override events
17564
+ setEvents: events => injectState.set(state => ({
17565
+ ...state,
17566
+ events: {
17567
+ ...state.events,
17568
+ ...events
17569
+ }
17570
+ }))
17571
+ };
17572
+ });
17573
+ const usePortalStore = React.useMemo(() => {
17574
+ // Create a mirrored store, based on the previous root with a few overrides ...
17575
+ const store = createWithEqualityFn((set, get) => ({
17576
+ ...rest,
17577
+ set,
17578
+ get
17579
+ }));
17580
+
17581
+ // Subscribe to previous root-state and copy changes over to the mirrored portal-state
17582
+ const onMutate = prev => store.setState(state => inject.current(prev, state));
17583
+ onMutate(previousRoot.getState());
17584
+ previousRoot.subscribe(onMutate);
17585
+ return store;
17586
+ // eslint-disable-next-line react-hooks/exhaustive-deps
17587
+ }, [previousRoot, container]);
17588
+ return (
17589
+ /*#__PURE__*/
17590
+ // @ts-ignore, reconciler types are not maintained
17591
+ jsx(Fragment, {
17592
+ children: reconciler.createPortal( /*#__PURE__*/jsx(context.Provider, {
17593
+ value: usePortalStore,
17594
+ children: children
17595
+ }), usePortalStore, null)
17596
+ })
17597
+ );
17598
+ }
17599
+
17477
17600
  const DOM_EVENTS = {
17478
17601
  onClick: ['click', false],
17479
17602
  onContextMenu: ['contextmenu', false],
@@ -17560,4 +17683,4 @@ function createPointerEvents(store) {
17560
17683
  };
17561
17684
  }
17562
17685
 
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, unmountComponentAtNode as f, createPointerEvents as g, createEvents as h, isRef as i, flushGlobalEffects as j, addEffect as k, addAfterEffect as l, addTail as m, invalidate as n, advance as o, createPortal as p, flushSync 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 };
17686
+ 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 };