@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.
@@ -2,10 +2,10 @@
2
2
 
3
3
  var React = require('react');
4
4
  var THREE = require('three');
5
+ var jsxRuntime = require('react/jsx-runtime');
5
6
  var traditional = require('zustand/traditional');
6
7
  var suspendReact = require('suspend-react');
7
8
  var w1 = require('scheduler');
8
- var jsxRuntime = require('react/jsx-runtime');
9
9
  var itsFine = require('its-fine');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
@@ -95,23 +95,29 @@ function useGate() {
95
95
  }) : null;
96
96
  return [gate, waitFor];
97
97
  }
98
+
99
+ // Activity is optional on React 19.0/19.1. A computed key also supports strict ESM bundlers.
100
+ const useActivityBridge = React__namespace['Activity' + ''] ? itsFine.useActivityBridge : () => React__namespace.Fragment;
98
101
  /**
99
- * Bridges renderer Context and StrictMode from a primary renderer.
102
+ * Bridges Context, StrictMode, and Activity visibility from a primary renderer.
100
103
  */
101
104
  function useBridge() {
102
105
  const fiber = itsFine.useFiber();
103
106
  const ContextBridge = itsFine.useContextBridge();
107
+ const ActivityBridge = useActivityBridge();
104
108
  return React__namespace.useMemo(() => ({
105
109
  children
106
110
  }) => {
107
111
  const strict = !!itsFine.traverseFiber(fiber, true, node => node.type === React__namespace.StrictMode);
108
112
  const Root = strict ? React__namespace.StrictMode : React__namespace.Fragment;
109
113
  return /*#__PURE__*/jsxRuntime.jsx(Root, {
110
- children: /*#__PURE__*/jsxRuntime.jsx(ContextBridge, {
111
- children: children
114
+ children: /*#__PURE__*/jsxRuntime.jsx(ActivityBridge, {
115
+ children: /*#__PURE__*/jsxRuntime.jsx(ContextBridge, {
116
+ children: children
117
+ })
112
118
  })
113
119
  });
114
- }, [fiber, ContextBridge]);
120
+ }, [fiber, ContextBridge, ActivityBridge]);
115
121
  }
116
122
  function Block({
117
123
  set
@@ -257,6 +263,7 @@ function prepare(target, root, type, props) {
257
263
  object,
258
264
  eventCount: 0,
259
265
  handlers: {},
266
+ previousVisible: undefined,
260
267
  isHidden: false
261
268
  };
262
269
  if (object) object.__r3f = instance;
@@ -440,6 +447,12 @@ function applyProps(object, props) {
440
447
  // Ignore setting undefined props
441
448
  // https://github.com/pmndrs/react-three-fiber/issues/274
442
449
  if (value === undefined) continue;
450
+
451
+ // Update the visibility to restore without revealing a hidden instance.
452
+ if (prop === 'visible' && instance != null && instance.isHidden && instance.previousVisible !== undefined) {
453
+ instance.previousVisible = value;
454
+ continue;
455
+ }
443
456
  let {
444
457
  root,
445
458
  key,
@@ -1364,9 +1377,326 @@ useLoader.clear = function (loader, input) {
1364
1377
  e = 2,
1365
1378
  i$1 = 268435456;
1366
1379
 
1380
+ // Shim for OffscreenCanvas since it was removed from DOM types
1381
+ // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54988
1382
+
1383
+ /** What a root has applied, so its next configuration applies only what changed */
1384
+
1385
+ const shallow = {
1386
+ objects: 'shallow'
1387
+ };
1388
+
1389
+ /** The renderer to configure, or a promise for one from an async factory */
1390
+ function createRenderer(canvas, gl) {
1391
+ const defaults = {
1392
+ canvas,
1393
+ powerPreference: 'high-performance',
1394
+ antialias: true,
1395
+ alpha: true
1396
+ };
1397
+ if (typeof gl === 'function') return gl(defaults);
1398
+ if (isRenderer(gl)) return gl;
1399
+ return new THREE__namespace.WebGLRenderer({
1400
+ ...defaults,
1401
+ ...gl
1402
+ });
1403
+ }
1404
+ function computeInitialSize(canvas, size) {
1405
+ if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1406
+ const {
1407
+ width,
1408
+ height,
1409
+ top,
1410
+ left
1411
+ } = canvas.parentElement.getBoundingClientRect();
1412
+ return {
1413
+ width,
1414
+ height,
1415
+ top,
1416
+ left
1417
+ };
1418
+ } else if (!size && typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
1419
+ return {
1420
+ width: canvas.width,
1421
+ height: canvas.height,
1422
+ top: 0,
1423
+ left: 0
1424
+ };
1425
+ }
1426
+ return {
1427
+ width: 0,
1428
+ height: 0,
1429
+ top: 0,
1430
+ left: 0,
1431
+ ...size
1432
+ };
1433
+ }
1434
+
1435
+ /**
1436
+ * Applies the inputs that changed since the last configuration applied in full, to a root whose
1437
+ * renderer is installed. A runtime change, such as setFrameloop or an edit to gl.shadowMap, lasts
1438
+ * until its input changes. The pixel ratio instead follows the device on every call. Objects and
1439
+ * arrays compare shallowly, so an equal inline value is unchanged.
1440
+ */
1441
+ function applyRootConfiguration({
1442
+ store,
1443
+ configuration
1444
+ }, canvas, props) {
1445
+ const {
1446
+ gl: glConfig,
1447
+ scene: sceneOptions,
1448
+ camera: cameraOptions,
1449
+ shadows = false,
1450
+ linear = false,
1451
+ flat = false,
1452
+ legacy = false,
1453
+ frameloop = 'always',
1454
+ dpr = [1, 2],
1455
+ performance,
1456
+ onPointerMissed
1457
+ } = props;
1458
+ const size = computeInitialSize(canvas, props.size);
1459
+ const next = {
1460
+ ...props,
1461
+ shadows,
1462
+ linear,
1463
+ flat,
1464
+ legacy,
1465
+ frameloop,
1466
+ size
1467
+ };
1468
+ const state = store.getState();
1469
+ const gl = state.gl;
1470
+ const last = configuration.previous;
1471
+ const changed = key => !last || !is.equ(next[key], last[key], shallow);
1472
+ // A failure can leave inputs partly applied, so the next configuration applies all of them
1473
+ configuration.previous = undefined;
1474
+
1475
+ // Set up raycaster (one time only!)
1476
+ let raycaster = state.raycaster;
1477
+ if (!raycaster) state.set({
1478
+ raycaster: raycaster = new THREE__namespace.Raycaster()
1479
+ });
1480
+ if (changed('raycaster')) {
1481
+ var _props$raycaster;
1482
+ const {
1483
+ params,
1484
+ ...options
1485
+ } = (_props$raycaster = props.raycaster) != null ? _props$raycaster : {};
1486
+ applyProps(raycaster, {
1487
+ ...options,
1488
+ params: {
1489
+ ...raycaster.params,
1490
+ ...params
1491
+ }
1492
+ });
1493
+ }
1494
+
1495
+ // Create default camera, don't overwrite any user-set state
1496
+ if (!state.camera || state.camera === configuration.camera && changed('camera')) {
1497
+ configuration.camera = cameraOptions;
1498
+ const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
1499
+ const camera = isCamera ? cameraOptions : props.orthographic ? new THREE__namespace.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE__namespace.PerspectiveCamera(75, 0, 0.1, 1000);
1500
+ if (!isCamera) {
1501
+ camera.position.z = 5;
1502
+ if (cameraOptions) {
1503
+ applyProps(camera, cameraOptions);
1504
+ // Preserve user-defined frustum if possible
1505
+ // https://github.com/pmndrs/react-three-fiber/issues/3160
1506
+ if (!camera.manual) {
1507
+ if ('aspect' in cameraOptions || 'left' in cameraOptions || 'right' in cameraOptions || 'bottom' in cameraOptions || 'top' in cameraOptions) {
1508
+ camera.manual = true;
1509
+ camera.updateProjectionMatrix();
1510
+ }
1511
+ }
1512
+ }
1513
+ // Always look at center by default
1514
+ if (!state.camera && !(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
1515
+ }
1516
+ state.set({
1517
+ camera
1518
+ });
1519
+
1520
+ // Configure raycaster
1521
+ // https://github.com/pmndrs/react-xr/issues/300
1522
+ raycaster.camera = camera;
1523
+ }
1524
+
1525
+ // Set up scene (one time only!)
1526
+ if (!state.scene) {
1527
+ let scene;
1528
+ if (sceneOptions != null && sceneOptions.isScene) {
1529
+ scene = sceneOptions;
1530
+ prepare(scene, store, '', {});
1531
+ } else {
1532
+ scene = new THREE__namespace.Scene();
1533
+ prepare(scene, store, '', {});
1534
+ if (sceneOptions) applyProps(scene, sceneOptions);
1535
+ }
1536
+ state.set({
1537
+ scene
1538
+ });
1539
+ }
1540
+
1541
+ // Store events internally
1542
+ if (props.events && !state.events.handlers) state.set({
1543
+ events: props.events(store)
1544
+ });
1545
+ if (changed('size')) state.setSize(size.width, size.height, size.top, size.left);
1546
+ // The ratio follows window.devicePixelRatio, which changes with browser zoom or another display
1547
+ if (state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
1548
+ if (changed('frameloop')) state.setFrameloop(frameloop);
1549
+ if (changed('onPointerMissed')) state.set({
1550
+ onPointerMissed
1551
+ });
1552
+ // Performance options merge, so removing them keeps the current settings
1553
+ if (performance && changed('performance')) {
1554
+ state.set(state => ({
1555
+ performance: {
1556
+ ...state.performance,
1557
+ ...performance
1558
+ }
1559
+ }));
1560
+ }
1561
+
1562
+ // Set up XR (one time only!)
1563
+ if (!state.xr) {
1564
+ var _gl$xr;
1565
+ // Handle frame behavior in WebXR
1566
+ const handleXRFrame = (timestamp, frame) => {
1567
+ const state = store.getState();
1568
+ if (state.frameloop === 'never') return;
1569
+ advance(timestamp, true, state, frame);
1570
+ };
1571
+
1572
+ // Toggle render switching on session
1573
+ const handleSessionChange = () => {
1574
+ const state = store.getState();
1575
+ state.gl.xr.enabled = state.gl.xr.isPresenting;
1576
+ state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
1577
+ if (!state.gl.xr.isPresenting) invalidate(state);
1578
+ };
1579
+
1580
+ // WebXR session manager
1581
+ const xr = {
1582
+ connect() {
1583
+ const gl = store.getState().gl;
1584
+ gl.xr.addEventListener('sessionstart', handleSessionChange);
1585
+ gl.xr.addEventListener('sessionend', handleSessionChange);
1586
+ },
1587
+ disconnect() {
1588
+ const gl = store.getState().gl;
1589
+ gl.xr.removeEventListener('sessionstart', handleSessionChange);
1590
+ gl.xr.removeEventListener('sessionend', handleSessionChange);
1591
+ }
1592
+ };
1593
+
1594
+ // Subscribe to WebXR session events
1595
+ if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
1596
+ state.set({
1597
+ xr
1598
+ });
1599
+ }
1600
+
1601
+ // Set shadowmap
1602
+ if (gl.shadowMap && changed('shadows')) {
1603
+ const oldEnabled = gl.shadowMap.enabled;
1604
+ const oldType = gl.shadowMap.type;
1605
+ gl.shadowMap.enabled = !!shadows;
1606
+ if (is.boo(shadows)) {
1607
+ gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
1608
+ } else if (is.str(shadows)) {
1609
+ var _types$shadows;
1610
+ const types = {
1611
+ basic: THREE__namespace.BasicShadowMap,
1612
+ percentage: THREE__namespace.PCFShadowMap,
1613
+ soft: THREE__namespace.PCFSoftShadowMap,
1614
+ variance: THREE__namespace.VSMShadowMap
1615
+ };
1616
+ gl.shadowMap.type = (_types$shadows = types[shadows]) != null ? _types$shadows : THREE__namespace.PCFSoftShadowMap;
1617
+ } else if (is.obj(shadows)) {
1618
+ Object.assign(gl.shadowMap, shadows);
1619
+ }
1620
+ if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
1621
+ }
1622
+
1623
+ // Explicit renderer options take precedence over the color shorthands
1624
+ const glProps = glConfig && !is.fun(glConfig) && !isRenderer(glConfig) ? glConfig : undefined;
1625
+ if (changed('legacy')) {
1626
+ THREE__namespace.ColorManagement.enabled = !legacy;
1627
+ state.set({
1628
+ legacy
1629
+ });
1630
+ }
1631
+ if (changed('linear')) {
1632
+ if ((glProps == null ? void 0 : glProps.outputColorSpace) === undefined) {
1633
+ gl.outputColorSpace = linear ? THREE__namespace.LinearSRGBColorSpace : THREE__namespace.SRGBColorSpace;
1634
+ }
1635
+ state.set({
1636
+ linear
1637
+ });
1638
+ }
1639
+ if (changed('flat')) {
1640
+ if ((glProps == null ? void 0 : glProps.toneMapping) === undefined) gl.toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
1641
+ state.set({
1642
+ flat
1643
+ });
1644
+ }
1645
+
1646
+ // Set gl props
1647
+ if (glProps && changed('gl')) applyProps(gl, glProps);
1648
+ configuration.previous = next;
1649
+ }
1650
+
1651
+ const isPromiseLike = value => typeof (value == null ? void 0 : value.then) === 'function';
1652
+
1653
+ /** A promise tagged with its state, the protocol React's `use` reads */
1654
+
1655
+ const fulfilled = value => Object.assign(Promise.resolve(value), {
1656
+ status: 'fulfilled',
1657
+ value
1658
+ });
1659
+ const rejected = reason => {
1660
+ const promise = Promise.reject(reason);
1661
+ promise.catch(() => {}); // the reason is read from `status`
1662
+ return Object.assign(promise, {
1663
+ status: 'rejected',
1664
+ reason
1665
+ });
1666
+ };
1667
+
1668
+ /** A pending promise, settled once from outside. Its `status` updates the moment it settles */
1669
+ const deferred = () => {
1670
+ let resolve;
1671
+ let reject;
1672
+ const promise = new Promise((yes, no) => {
1673
+ resolve = value => {
1674
+ Object.assign(promise, {
1675
+ status: 'fulfilled',
1676
+ value
1677
+ });
1678
+ yes(value);
1679
+ };
1680
+ reject = reason => {
1681
+ Object.assign(promise, {
1682
+ status: 'rejected',
1683
+ reason
1684
+ });
1685
+ no(reason);
1686
+ };
1687
+ });
1688
+ promise.status = 'pending';
1689
+ promise.catch(() => {}); // the reason is read from `status`
1690
+ return {
1691
+ promise,
1692
+ resolve,
1693
+ reject
1694
+ };
1695
+ };
1696
+
1367
1697
  var packageData = {
1368
1698
  name: "@react-three/fiber",
1369
- version: "9.8.0",
1699
+ version: "9.8.1",
1370
1700
  description: "A React renderer for Threejs",
1371
1701
  keywords: [
1372
1702
  "react",
@@ -1417,7 +1747,7 @@ var packageData = {
1417
1747
  "@types/webxr": "*",
1418
1748
  "base64-js": "^1.5.1",
1419
1749
  buffer: "^6.0.3",
1420
- "its-fine": "^2.0.0",
1750
+ "its-fine": "^2.1.1",
1421
1751
  "react-use-measure": "^2.1.7",
1422
1752
  scheduler: "^0.28.0",
1423
1753
  "suspend-react": "^0.1.3",
@@ -16195,6 +16525,7 @@ function hideInstance(instance) {
16195
16525
  if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
16196
16526
  detach(instance.parent, instance);
16197
16527
  } else if (isObject3D(instance.object)) {
16528
+ instance.previousVisible = instance.object.visible;
16198
16529
  instance.object.visible = false;
16199
16530
  }
16200
16531
  instance.isHidden = true;
@@ -16206,9 +16537,11 @@ function unhideInstance(instance) {
16206
16537
  var _instance$parent2;
16207
16538
  if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
16208
16539
  attach(instance.parent, instance);
16209
- } else if (isObject3D(instance.object) && instance.props.visible !== false) {
16210
- instance.object.visible = true;
16540
+ } else if (isObject3D(instance.object)) {
16541
+ var _instance$previousVis;
16542
+ instance.object.visible = (_instance$previousVis = instance.previousVisible) != null ? _instance$previousVis : true;
16211
16543
  }
16544
+ instance.previousVisible = undefined;
16212
16545
  instance.isHidden = false;
16213
16546
  invalidateInstance(instance);
16214
16547
  }
@@ -16753,76 +17086,49 @@ const reconciler = /* @__PURE__ */createReconciler({
16753
17086
  getSuspendedCommitReason: (_state, _rootContainer) => null
16754
17087
  });
16755
17088
 
16756
- const isPromiseLike = value => typeof (value == null ? void 0 : value.then) === 'function';
16757
-
16758
- /** A promise tagged with its state, the protocol React's `use` reads */
16759
-
16760
- const fulfilled = value => Object.assign(Promise.resolve(value), {
16761
- status: 'fulfilled',
16762
- value
16763
- });
16764
- const rejected = reason => {
16765
- const promise = Promise.reject(reason);
16766
- promise.catch(() => {}); // the reason is read from `status`
16767
- return Object.assign(promise, {
16768
- status: 'rejected',
16769
- reason
16770
- });
16771
- };
16772
-
16773
- /** Tags `promise` in place once it settles. */
16774
- const tracked = promise => {
16775
- const result = Promise.resolve(promise);
16776
- result.status = 'pending';
16777
- // Statement bodies: returning `result` would make this derived promise adopt its rejection
16778
- result.then(value => {
16779
- Object.assign(result, {
16780
- status: 'fulfilled',
16781
- value
16782
- });
16783
- }, reason => {
16784
- Object.assign(result, {
16785
- status: 'rejected',
16786
- reason
16787
- });
16788
- });
16789
- return result;
16790
- };
17089
+ /**
17090
+ * Force React to flush any updates inside the provided callback synchronously and immediately.
17091
+ * All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync).
17092
+ * Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without
17093
+ * having to revert to a non-React solution. Note: this will only flush updates within the `Canvas` root.
17094
+ */
17095
+ function flushSync(fn) {
17096
+ // @ts-ignore - reconciler types are not maintained
17097
+ return reconciler.flushSyncFromReconciler(fn);
17098
+ }
16791
17099
 
16792
17100
  const _roots = new Map();
16793
- const shallowLoose = {
16794
- objects: 'shallow',
16795
- strict: false
16796
- };
16797
- function computeInitialSize(canvas, size) {
16798
- if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
16799
- const {
16800
- width,
16801
- height,
16802
- top,
16803
- left
16804
- } = canvas.parentElement.getBoundingClientRect();
16805
- return {
16806
- width,
16807
- height,
16808
- top,
16809
- left
16810
- };
16811
- } else if (!size && typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas) {
16812
- return {
16813
- width: canvas.width,
16814
- height: canvas.height,
16815
- top: 0,
16816
- left: 0
16817
- };
17101
+ /**
17102
+ * open -> closing -> disposed
17103
+ * | use
17104
+ * v
17105
+ * open
17106
+ *
17107
+ * Only closing can be cancelled. Each unmount carries a token, so only the latest one can dispose.
17108
+ */
17109
+ function transitionRoot(root, event) {
17110
+ const state = root.state;
17111
+ switch (event.type) {
17112
+ case 'use':
17113
+ if (state.status === 'disposed') return false;
17114
+ root.state = {
17115
+ status: 'open'
17116
+ };
17117
+ return true;
17118
+ case 'unmount':
17119
+ if (state.status === 'disposed') return false;
17120
+ root.state = {
17121
+ status: 'closing',
17122
+ token: event.token
17123
+ };
17124
+ return true;
17125
+ case 'dispose':
17126
+ if (state.status !== 'closing' || state.token !== event.token) return false;
17127
+ root.state = {
17128
+ status: 'disposed'
17129
+ };
17130
+ return true;
16818
17131
  }
16819
- return {
16820
- width: 0,
16821
- height: 0,
16822
- top: 0,
16823
- left: 0,
16824
- ...size
16825
- };
16826
17132
  }
16827
17133
  function createRoot(canvas) {
16828
17134
  // Check against mistaken use of createRoot
@@ -16867,286 +17173,84 @@ function createRoot(canvas) {
16867
17173
  const root = prevRoot || {
16868
17174
  fiber,
16869
17175
  store,
16870
- unmountClaim: null,
16871
- ready: fulfilled(undefined)
17176
+ state: {
17177
+ status: 'open'
17178
+ },
17179
+ ready: fulfilled(undefined),
17180
+ configuration: {},
17181
+ ownsRenderer: false
16872
17182
  };
16873
17183
  if (!prevRoot) _roots.set(canvas, root);
16874
-
16875
- // Locals
16876
- let onCreated;
16877
- let lastCamera;
16878
17184
  let mounted = false;
16879
17185
  return {
16880
17186
  get ready() {
16881
17187
  return root.ready;
16882
17188
  },
16883
17189
  configure(props = {}) {
16884
- root.unmountClaim = null;
16885
- const {
16886
- gl: glConfig,
16887
- size: propsSize,
16888
- scene: sceneOptions,
16889
- events,
16890
- onCreated: onCreatedCallback,
16891
- shadows = false,
16892
- linear = false,
16893
- flat = false,
16894
- legacy = false,
16895
- orthographic = false,
16896
- frameloop = 'always',
16897
- dpr = [1, 2],
16898
- performance,
16899
- raycaster: raycastOptions,
16900
- camera: cameraOptions,
16901
- onPointerMissed
16902
- } = props;
16903
- const defaultProps = {
16904
- canvas: canvas,
16905
- powerPreference: 'high-performance',
16906
- antialias: true,
16907
- alpha: true
16908
- };
17190
+ if (!transitionRoot(root, {
17191
+ type: 'use'
17192
+ })) {
17193
+ return rejected(new Error('R3F: Cannot configure a root after disposal has started.'));
17194
+ }
16909
17195
 
16910
- // Synchronous, and needs the renderer to exist
16911
- const apply = customRenderer => {
17196
+ // Configuration applies in call order. Publishing first queues calls made while this one runs
17197
+ const previous = root.ready;
17198
+ const {
17199
+ promise,
17200
+ resolve,
17201
+ reject
17202
+ } = deferred();
17203
+ root.ready = promise;
17204
+ const apply = gl => {
16912
17205
  const state = store.getState();
16913
-
16914
17206
  // Set up renderer (one time only!)
16915
- const first = !state.gl;
16916
- let gl = state.gl;
16917
- if (first) {
16918
- gl = isRenderer(customRenderer) ? customRenderer : new THREE__namespace.WebGLRenderer({
16919
- ...defaultProps,
16920
- ...glConfig
16921
- });
16922
- state.set({
16923
- gl
16924
- });
16925
- }
16926
-
16927
- // Set up raycaster (one time only!)
16928
- let raycaster = state.raycaster;
16929
- if (!raycaster) state.set({
16930
- raycaster: raycaster = new THREE__namespace.Raycaster()
16931
- });
16932
-
16933
- // Set raycaster options
16934
- const {
16935
- params,
16936
- ...options
16937
- } = raycastOptions || {};
16938
- if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, {
16939
- ...options
16940
- });
16941
- if (!is.equ(params, raycaster.params, shallowLoose)) applyProps(raycaster, {
16942
- params: {
16943
- ...raycaster.params,
16944
- ...params
16945
- }
16946
- });
16947
-
16948
- // Create default camera, don't overwrite any user-set state
16949
- if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
16950
- lastCamera = cameraOptions;
16951
- const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
16952
- const camera = isCamera ? cameraOptions : orthographic ? new THREE__namespace.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE__namespace.PerspectiveCamera(75, 0, 0.1, 1000);
16953
- if (!isCamera) {
16954
- camera.position.z = 5;
16955
- if (cameraOptions) {
16956
- applyProps(camera, cameraOptions);
16957
- // Preserve user-defined frustum if possible
16958
- // https://github.com/pmndrs/react-three-fiber/issues/3160
16959
- if (!camera.manual) {
16960
- if ('aspect' in cameraOptions || 'left' in cameraOptions || 'right' in cameraOptions || 'bottom' in cameraOptions || 'top' in cameraOptions) {
16961
- camera.manual = true;
16962
- camera.updateProjectionMatrix();
16963
- }
16964
- }
16965
- }
16966
- // Always look at center by default
16967
- if (!state.camera && !(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
16968
- }
16969
- state.set({
16970
- camera
16971
- });
16972
-
16973
- // Configure raycaster
16974
- // https://github.com/pmndrs/react-xr/issues/300
16975
- raycaster.camera = camera;
16976
- }
16977
-
16978
- // Set up scene (one time only!)
16979
- if (!state.scene) {
16980
- let scene;
16981
- if (sceneOptions != null && sceneOptions.isScene) {
16982
- scene = sceneOptions;
16983
- prepare(scene, store, '', {});
16984
- } else {
16985
- scene = new THREE__namespace.Scene();
16986
- prepare(scene, store, '', {});
16987
- if (sceneOptions) applyProps(scene, sceneOptions);
16988
- }
17207
+ if (!state.gl) {
17208
+ // R3F owns what it builds, a factory's result included, since it calls the factory
17209
+ // once per root. A renderer instance belongs to the caller
17210
+ root.ownsRenderer = !isRenderer(props.gl);
16989
17211
  state.set({
16990
- scene
17212
+ gl: gl
16991
17213
  });
16992
17214
  }
16993
-
16994
- // Store events internally
16995
- if (events && !state.events.handlers) state.set({
16996
- events: events(store)
16997
- });
16998
- // Check size, allow it to take on container bounds initially
16999
- const size = computeInitialSize(canvas, propsSize);
17000
- if (!is.equ(size, state.size, shallowLoose)) {
17001
- state.setSize(size.width, size.height, size.top, size.left);
17002
- }
17003
- // Check pixelratio
17004
- if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
17005
- // Check frameloop
17006
- if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
17007
- // Check pointer missed
17008
- if (!state.onPointerMissed) state.set({
17009
- onPointerMissed
17010
- });
17011
- // Check performance
17012
- if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
17013
- performance: {
17014
- ...state.performance,
17015
- ...performance
17016
- }
17017
- }));
17018
-
17019
- // Set up XR (one time only!)
17020
- if (!state.xr) {
17021
- var _gl$xr;
17022
- // Handle frame behavior in WebXR
17023
- const handleXRFrame = (timestamp, frame) => {
17024
- const state = store.getState();
17025
- if (state.frameloop === 'never') return;
17026
- advance(timestamp, true, state, frame);
17027
- };
17028
-
17029
- // Toggle render switching on session
17030
- const handleSessionChange = () => {
17031
- const state = store.getState();
17032
- state.gl.xr.enabled = state.gl.xr.isPresenting;
17033
- state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
17034
- if (!state.gl.xr.isPresenting) invalidate(state);
17035
- };
17036
-
17037
- // WebXR session manager
17038
- const xr = {
17039
- connect() {
17040
- const gl = store.getState().gl;
17041
- gl.xr.addEventListener('sessionstart', handleSessionChange);
17042
- gl.xr.addEventListener('sessionend', handleSessionChange);
17043
- },
17044
- disconnect() {
17045
- const gl = store.getState().gl;
17046
- gl.xr.removeEventListener('sessionstart', handleSessionChange);
17047
- gl.xr.removeEventListener('sessionend', handleSessionChange);
17048
- }
17049
- };
17050
-
17051
- // Subscribe to WebXR session events
17052
- if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
17053
- state.set({
17054
- xr
17055
- });
17056
- }
17057
-
17058
- // Set shadowmap
17059
- if (gl.shadowMap) {
17060
- const oldEnabled = gl.shadowMap.enabled;
17061
- const oldType = gl.shadowMap.type;
17062
- gl.shadowMap.enabled = !!shadows;
17063
- if (is.boo(shadows)) {
17064
- gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
17065
- } else if (is.str(shadows)) {
17066
- var _types$shadows;
17067
- const types = {
17068
- basic: THREE__namespace.BasicShadowMap,
17069
- percentage: THREE__namespace.PCFShadowMap,
17070
- soft: THREE__namespace.PCFSoftShadowMap,
17071
- variance: THREE__namespace.VSMShadowMap
17072
- };
17073
- gl.shadowMap.type = (_types$shadows = types[shadows]) != null ? _types$shadows : THREE__namespace.PCFSoftShadowMap;
17074
- } else if (is.obj(shadows)) {
17075
- Object.assign(gl.shadowMap, shadows);
17076
- }
17077
- if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
17078
- }
17079
- THREE__namespace.ColorManagement.enabled = !legacy;
17080
-
17081
- // Set color space and tonemapping preferences
17082
- if (first) {
17083
- gl.outputColorSpace = linear ? THREE__namespace.LinearSRGBColorSpace : THREE__namespace.SRGBColorSpace;
17084
- gl.toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
17085
- }
17086
-
17087
- // Update color management state
17088
- if (state.legacy !== legacy) state.set(() => ({
17089
- legacy
17090
- }));
17091
- if (state.linear !== linear) state.set(() => ({
17092
- linear
17093
- }));
17094
- if (state.flat !== flat) state.set(() => ({
17095
- flat
17096
- }));
17097
-
17098
- // Set gl props
17099
- if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig);
17100
-
17101
- // Set locals
17102
- onCreated = onCreatedCallback;
17103
- };
17104
- const fail = error => {
17105
- const failure = rejected(error);
17106
- root.ready = failure;
17107
- return failure;
17215
+ applyRootConfiguration(root, canvas, props);
17216
+ resolve(this);
17108
17217
  };
17109
- const run = customRenderer => {
17218
+ const run = () => {
17110
17219
  try {
17111
- apply(customRenderer);
17112
- root.ready = fulfilled(undefined);
17113
- return fulfilled(this);
17220
+ var _store$getState$gl;
17221
+ const gl = (_store$getState$gl = store.getState().gl) != null ? _store$getState$gl : createRenderer(canvas, props.gl);
17222
+ // Only an async renderer factory makes configuration asynchronous
17223
+ if (isPromiseLike(gl)) Promise.resolve(gl).then(apply).catch(reject);else apply(gl);
17114
17224
  } catch (error) {
17115
- return fail(error);
17225
+ reject(error);
17116
17226
  }
17117
17227
  };
17118
-
17119
- // The renderer exists, so applying props is synchronous
17120
- if (store.getState().gl) return run();
17121
-
17122
- // Another configure is creating it. Chaining keeps them in call order
17123
- if (root.ready.status === 'pending') return tracked(root.ready.then(() => run()));
17124
-
17125
- // Only an async factory makes configure asynchronous
17126
- let customRenderer;
17127
- try {
17128
- customRenderer = typeof glConfig === 'function' ? glConfig(defaultProps) : glConfig;
17129
- } catch (error) {
17130
- return fail(error);
17131
- }
17132
- if (!isPromiseLike(customRenderer)) return run(customRenderer);
17133
- const configuring = tracked(customRenderer.then(run, fail));
17134
- root.ready = configuring;
17135
- return configuring;
17228
+ if (previous.status === 'pending') previous.then(run, reject);else run();
17229
+ return promise;
17136
17230
  },
17137
17231
  render(children) {
17138
- if (_roots.get(canvas) !== root) return store;
17139
- // Using the root cancels a teardown waiting on React
17140
- root.unmountClaim = null;
17141
- const element = /*#__PURE__*/jsxRuntime.jsx(Provider, {
17142
- store: store,
17143
- children: children,
17144
- onCreated: onCreated,
17145
- rootElement: canvas
17146
- });
17232
+ if (!transitionRoot(root, {
17233
+ type: 'use'
17234
+ })) return store;
17235
+
17236
+ // The root has to be configured before it can be rendered
17237
+ if (!store.getState().gl && root.ready.status === 'fulfilled') this.configure();
17238
+ if (root.ready.status === 'rejected') throw root.ready.reason;
17147
17239
  const commit = () => {
17148
- // The root may have been unmounted or claimed while we waited
17149
- if (_roots.get(canvas) !== root || root.unmountClaim) return;
17240
+ var _root$configuration$p;
17241
+ if (root.state.status !== 'open' || root.ready.status === 'rejected') return;
17242
+ // Wait for all queued configuration, including any queued while waiting
17243
+ if (root.ready.status === 'pending') {
17244
+ root.ready.then(commit, logRecoverableError);
17245
+ return;
17246
+ }
17247
+ const onCreated = (_root$configuration$p = root.configuration.previous) == null ? void 0 : _root$configuration$p.onCreated;
17248
+ const element = /*#__PURE__*/jsxRuntime.jsx(Provider, {
17249
+ store: store,
17250
+ children: children,
17251
+ onCreated: onCreated,
17252
+ rootElement: canvas
17253
+ });
17150
17254
  if (mounted) {
17151
17255
  reconciler.updateContainer(element, fiber, null, noop);
17152
17256
  return;
@@ -17158,22 +17262,11 @@ function createRoot(canvas) {
17158
17262
  // @ts-ignore - reconciler types are not maintained
17159
17263
  reconciler.flushSyncWork();
17160
17264
  };
17161
-
17162
- // The root has to be configured before it can be rendered
17163
- if (!store.getState().gl && root.ready.status === 'fulfilled') this.configure();
17164
- switch (root.ready.status) {
17165
- case 'pending':
17166
- root.ready.then(commit, logRecoverableError);
17167
- break;
17168
- case 'rejected':
17169
- throw root.ready.reason;
17170
- default:
17171
- commit();
17172
- }
17265
+ commit();
17173
17266
  return store;
17174
17267
  },
17175
17268
  unmount() {
17176
- unmountComponentAtNode(canvas);
17269
+ if (_roots.get(canvas) === root) unmountComponentAtNode(canvas);
17177
17270
  }
17178
17271
  };
17179
17272
  }
@@ -17204,147 +17297,81 @@ function Provider({
17204
17297
  children: children
17205
17298
  });
17206
17299
  }
17207
- function unmountComponentAtNode(canvas, callback) {
17208
- const root = _roots.get(canvas);
17209
- if (!root) return;
17210
-
17211
- // Cleared by configure and render, which cancels the teardown
17212
- const claim = root.unmountClaim = Symbol('unmount');
17213
- reconciler.updateContainer(null, root.fiber, null, () => {
17214
- if (root.unmountClaim !== claim) return;
17215
-
17216
- // Effect cleanups flush before the next update
17217
- reconciler.updateContainer(null, root.fiber, null, () => {
17218
- if (root.unmountClaim !== claim) return;
17219
- const teardown = () => {
17220
- if (root.unmountClaim !== claim) return;
17221
- root.unmountClaim = null;
17222
- const state = root.store.getState();
17223
- state.internal.active = false;
17224
- try {
17225
- var _state$gl, _state$gl$renderLists, _state$gl2, _state$gl3;
17226
- state.events.disconnect == null ? void 0 : state.events.disconnect();
17227
- (_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();
17228
- (_state$gl2 = state.gl) == null ? void 0 : _state$gl2.forceContextLoss == null ? void 0 : _state$gl2.forceContextLoss();
17229
- if ((_state$gl3 = state.gl) != null && _state$gl3.xr) state.xr.disconnect();
17230
- dispose(state.scene);
17231
- } catch (e) {
17232
- /* ... */
17233
- }
17234
- _roots.delete(canvas);
17235
- if (callback) callback(canvas);
17236
- };
17237
17300
 
17238
- // A renderer still being created has to exist before it can be disposed
17239
- if (root.ready.status === 'pending') root.ready.then(teardown, teardown);else teardown();
17240
- });
17301
+ /**
17302
+ * Frees a renderer R3F built. WebGLRenderer.dispose() releases programs and caches but keeps its
17303
+ * context until garbage collection, and browsers cap live WebGL contexts, so the context is lost
17304
+ * after it. A WebGPURenderer (from a factory) releases its device and context inside dispose().
17305
+ */
17306
+ function disposeRenderer(gl) {
17307
+ // Avoid starting initialization through three's dispose(). A factory must await init()
17308
+ // before returning its renderer so readiness includes initialization and teardown can wait.
17309
+ if ((gl.hasInitialized == null ? void 0 : gl.hasInitialized()) === false) return;
17310
+ const disposed = attempt(() => {
17311
+ var _gl$renderLists;
17312
+ return gl.dispose ? gl.dispose() : (_gl$renderLists = gl.renderLists) == null ? void 0 : _gl$renderLists.dispose == null ? void 0 : _gl$renderLists.dispose();
17241
17313
  });
17314
+ // WebGPURenderer.dispose() is async from three r186
17315
+ if (isPromiseLike(disposed)) {
17316
+ return Promise.resolve(disposed).catch(error => console.warn('[R3F] Error disposing renderer', error)).then(() => attempt(() => gl.forceContextLoss == null ? void 0 : gl.forceContextLoss()));
17317
+ }
17318
+ attempt(() => gl.forceContextLoss == null ? void 0 : gl.forceContextLoss());
17242
17319
  }
17243
- function createPortal(children, container, state) {
17244
- return /*#__PURE__*/jsxRuntime.jsx(Portal, {
17245
- children: children,
17246
- container: container,
17247
- state: state
17248
- });
17320
+ function attempt(step) {
17321
+ try {
17322
+ return step();
17323
+ } catch (error) {
17324
+ console.warn('[R3F] Error while unmounting root; teardown may be incomplete:', error);
17325
+ }
17249
17326
  }
17250
- function Portal({
17251
- state = {},
17252
- children,
17253
- container
17254
- }) {
17255
- /** This has to be a component because it would not be able to call useThree/useStore otherwise since
17256
- * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
17257
- * the "R3F hooks can only be used within the Canvas component!" warning:
17258
- * <Canvas>
17259
- * {createPortal(...)} */
17260
- const {
17261
- events,
17262
- size,
17263
- ...rest
17264
- } = state;
17265
- const previousRoot = useStore();
17266
- const [raycaster] = React__namespace.useState(() => new THREE__namespace.Raycaster());
17267
- const [pointer] = React__namespace.useState(() => new THREE__namespace.Vector2());
17268
- const inject = useMutableCallback((rootState, injectState) => {
17269
- let viewport = undefined;
17270
- if (injectState.camera && size) {
17271
- const camera = injectState.camera;
17272
- // Calculate the override viewport, if present
17273
- viewport = rootState.viewport.getCurrentViewport(camera, new THREE__namespace.Vector3(), size);
17274
- // Update the portal camera, if it differs from the previous layer
17275
- if (camera !== rootState.camera) updateCamera(camera, size);
17327
+ function unmountComponentAtNode(canvas, callback) {
17328
+ const root = _roots.get(canvas);
17329
+ const token = Symbol('unmount');
17330
+ if (!root || !transitionRoot(root, {
17331
+ type: 'unmount',
17332
+ token
17333
+ })) return;
17334
+ const teardown = () => {
17335
+ var _state$gl;
17336
+ // Configuration accepted before the unmount finishes first, so a renderer it creates is released too
17337
+ if (root.ready.status === 'pending') {
17338
+ root.ready.then(teardown, teardown);
17339
+ return;
17276
17340
  }
17277
- return {
17278
- // The intersect consists of the previous root state
17279
- ...rootState,
17280
- ...injectState,
17281
- // Portals have their own scene, which forms the root, a raycaster and a pointer
17282
- scene: container,
17283
- raycaster,
17284
- pointer,
17285
- mouse: pointer,
17286
- // Their previous root is the layer before it
17287
- previousRoot,
17288
- // Events, size and viewport can be overridden by the inject layer
17289
- events: {
17290
- ...rootState.events,
17291
- ...injectState.events,
17292
- ...events
17293
- },
17294
- size: {
17295
- ...rootState.size,
17296
- ...size
17297
- },
17298
- viewport: {
17299
- ...rootState.viewport,
17300
- ...viewport
17301
- },
17302
- // Layers are allowed to override events
17303
- setEvents: events => injectState.set(state => ({
17304
- ...state,
17305
- events: {
17306
- ...state.events,
17307
- ...events
17308
- }
17309
- }))
17310
- };
17311
- });
17312
- const usePortalStore = React__namespace.useMemo(() => {
17313
- // Create a mirrored store, based on the previous root with a few overrides ...
17314
- const store = traditional.createWithEqualityFn((set, get) => ({
17315
- ...rest,
17316
- set,
17317
- get
17318
- }));
17319
-
17320
- // Subscribe to previous root-state and copy changes over to the mirrored portal-state
17321
- const onMutate = prev => store.setState(state => inject.current(prev, state));
17322
- onMutate(previousRoot.getState());
17323
- previousRoot.subscribe(onMutate);
17324
- return store;
17325
- // eslint-disable-next-line react-hooks/exhaustive-deps
17326
- }, [previousRoot, container]);
17327
- return (
17328
- /*#__PURE__*/
17329
- // @ts-ignore, reconciler types are not maintained
17330
- jsxRuntime.jsx(jsxRuntime.Fragment, {
17331
- children: reconciler.createPortal( /*#__PURE__*/jsxRuntime.jsx(context.Provider, {
17332
- value: usePortalStore,
17333
- children: children
17334
- }), usePortalStore, null)
17335
- })
17336
- );
17337
- }
17341
+ // Refused when the root was used again since this unmount
17342
+ if (!transitionRoot(root, {
17343
+ type: 'dispose',
17344
+ token
17345
+ })) return;
17346
+ // Close the gates before invoking user cleanup code.
17347
+ _roots.delete(canvas);
17348
+ const state = root.store.getState();
17349
+ state.internal.active = false;
17338
17350
 
17339
- /**
17340
- * Force React to flush any updates inside the provided callback synchronously and immediately.
17341
- * All the same caveats documented for react-dom's `flushSync` apply here (see https://react.dev/reference/react-dom/flushSync).
17342
- * Nevertheless, sometimes one needs to render synchronously, for example to keep DOM and 3D changes in lock-step without
17343
- * having to revert to a non-React solution. Note: this will only flush updates within the `Canvas` root.
17344
- */
17345
- function flushSync(fn) {
17346
- // @ts-ignore - reconciler types are not maintained
17347
- return reconciler.flushSyncFromReconciler(fn);
17351
+ // Release what uses the renderer before the renderer itself
17352
+ attempt(() => state.events.disconnect == null ? void 0 : state.events.disconnect());
17353
+ if ((_state$gl = state.gl) != null && _state$gl.xr) attempt(() => state.xr.disconnect());
17354
+ if (state.scene) attempt(() => dispose(state.scene));
17355
+ const gl = state.gl;
17356
+ let disposal = undefined;
17357
+ if (gl && root.ownsRenderer) {
17358
+ disposal = attempt(() => disposeRenderer(gl));
17359
+ } else if (gl) {
17360
+ // A renderer passed in keeps its 9.x teardown: its context is lost but it is not disposed
17361
+ attempt(() => {
17362
+ var _gl$renderLists2;
17363
+ return (_gl$renderLists2 = gl.renderLists) == null ? void 0 : _gl$renderLists2.dispose == null ? void 0 : _gl$renderLists2.dispose();
17364
+ });
17365
+ attempt(() => gl.forceContextLoss == null ? void 0 : gl.forceContextLoss());
17366
+ }
17367
+ if (isPromiseLike(disposal)) disposal.then(() => callback == null ? void 0 : callback(canvas));else callback == null ? void 0 : callback(canvas);
17368
+ };
17369
+ reconciler.updateContainer(null, root.fiber, null, () => {
17370
+ // A root used again since the unmount keeps its new children
17371
+ if (root.state.status !== 'closing' || root.state.token !== token) return;
17372
+ // Effect cleanups flush before the next update
17373
+ reconciler.updateContainer(null, root.fiber, null, teardown);
17374
+ });
17348
17375
  }
17349
17376
 
17350
17377
  function createSubs(callback, subs) {
@@ -17499,6 +17526,102 @@ function advance(timestamp, runGlobalEffects = true, state, frame) {
17499
17526
  if (runGlobalEffects) flushGlobalEffects('after', timestamp);
17500
17527
  }
17501
17528
 
17529
+ function createPortal(children, container, state) {
17530
+ return /*#__PURE__*/jsxRuntime.jsx(Portal, {
17531
+ children: children,
17532
+ container: container,
17533
+ state: state
17534
+ });
17535
+ }
17536
+ function Portal({
17537
+ state = {},
17538
+ children,
17539
+ container
17540
+ }) {
17541
+ /** This has to be a component because it would not be able to call useThree/useStore otherwise since
17542
+ * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
17543
+ * the "R3F hooks can only be used within the Canvas component!" warning:
17544
+ * <Canvas>
17545
+ * {createPortal(...)} */
17546
+ const {
17547
+ events,
17548
+ size,
17549
+ ...rest
17550
+ } = state;
17551
+ const previousRoot = useStore();
17552
+ const [raycaster] = React__namespace.useState(() => new THREE__namespace.Raycaster());
17553
+ const [pointer] = React__namespace.useState(() => new THREE__namespace.Vector2());
17554
+ const inject = useMutableCallback((rootState, injectState) => {
17555
+ let viewport = undefined;
17556
+ if (injectState.camera && size) {
17557
+ const camera = injectState.camera;
17558
+ // Calculate the override viewport, if present
17559
+ viewport = rootState.viewport.getCurrentViewport(camera, new THREE__namespace.Vector3(), size);
17560
+ // Update the portal camera, if it differs from the previous layer
17561
+ if (camera !== rootState.camera) updateCamera(camera, size);
17562
+ }
17563
+ return {
17564
+ // The intersect consists of the previous root state
17565
+ ...rootState,
17566
+ ...injectState,
17567
+ // Portals have their own scene, which forms the root, a raycaster and a pointer
17568
+ scene: container,
17569
+ raycaster,
17570
+ pointer,
17571
+ mouse: pointer,
17572
+ // Their previous root is the layer before it
17573
+ previousRoot,
17574
+ // Events, size and viewport can be overridden by the inject layer
17575
+ events: {
17576
+ ...rootState.events,
17577
+ ...injectState.events,
17578
+ ...events
17579
+ },
17580
+ size: {
17581
+ ...rootState.size,
17582
+ ...size
17583
+ },
17584
+ viewport: {
17585
+ ...rootState.viewport,
17586
+ ...viewport
17587
+ },
17588
+ // Layers are allowed to override events
17589
+ setEvents: events => injectState.set(state => ({
17590
+ ...state,
17591
+ events: {
17592
+ ...state.events,
17593
+ ...events
17594
+ }
17595
+ }))
17596
+ };
17597
+ });
17598
+ const usePortalStore = React__namespace.useMemo(() => {
17599
+ // Create a mirrored store, based on the previous root with a few overrides ...
17600
+ const store = traditional.createWithEqualityFn((set, get) => ({
17601
+ ...rest,
17602
+ set,
17603
+ get
17604
+ }));
17605
+
17606
+ // Subscribe to previous root-state and copy changes over to the mirrored portal-state
17607
+ const onMutate = prev => store.setState(state => inject.current(prev, state));
17608
+ onMutate(previousRoot.getState());
17609
+ previousRoot.subscribe(onMutate);
17610
+ return store;
17611
+ // eslint-disable-next-line react-hooks/exhaustive-deps
17612
+ }, [previousRoot, container]);
17613
+ return (
17614
+ /*#__PURE__*/
17615
+ // @ts-ignore, reconciler types are not maintained
17616
+ jsxRuntime.jsx(jsxRuntime.Fragment, {
17617
+ children: reconciler.createPortal( /*#__PURE__*/jsxRuntime.jsx(context.Provider, {
17618
+ value: usePortalStore,
17619
+ children: children
17620
+ }), usePortalStore, null)
17621
+ })
17622
+ );
17623
+ }
17624
+
17502
17625
  const DOM_EVENTS = {
17503
17626
  onClick: ['click', false],
17504
17627
  onContextMenu: ['contextmenu', false],