diff --git a/.changeset/cool-avocados-eat.md b/.changeset/cool-avocados-eat.md
new file mode 100644
index 0000000000..4a1b01d3ab
--- /dev/null
+++ b/.changeset/cool-avocados-eat.md
@@ -0,0 +1,16 @@
+---
+'@react-spring/core': minor
+'@react-spring/animated': minor
+'@react-spring/parallax': minor
+'@react-spring/rafz': minor
+'react-spring': minor
+'@react-spring/shared': minor
+'@react-spring/types': minor
+'@react-spring/konva': minor
+'@react-spring/native': minor
+'@react-spring/three': minor
+'@react-spring/web': minor
+'@react-spring/zdog': minor
+---
+
+add experimental exit before enter
diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json
index c76ff9de3a..637e697001 100644
--- a/.codesandbox/ci.json
+++ b/.codesandbox/ci.json
@@ -1,28 +1,29 @@
{
"packages": ["packages/*", "targets/*"],
"sandboxes": [
+ "/demo/src/sandboxes/animating-auto",
"/demo/src/sandboxes/card",
- "/demo/src/sandboxes/goo-blobs",
- "/demo/src/sandboxes/flip-card",
- "/demo/src/sandboxes/slide",
- "/demo/src/sandboxes/draggable-list",
"/demo/src/sandboxes/cards-stack",
- "/demo/src/sandboxes/viewpager",
- "/demo/src/sandboxes/simple-transition",
+ "/demo/src/sandboxes/chain",
+ "/demo/src/sandboxes/css-keyframes",
+ "/demo/src/sandboxes/draggable-list",
+ "/demo/src/sandboxes/exit-before-enter",
+ "/demo/src/sandboxes/flip-card",
+ "/demo/src/sandboxes/goo-blobs",
"/demo/src/sandboxes/image-fade",
"/demo/src/sandboxes/list-reordering",
"/demo/src/sandboxes/masonry",
- "/demo/src/sandboxes/animating-auto",
"/demo/src/sandboxes/multistage-transition",
- "/demo/src/sandboxes/chain",
- "/demo/src/sandboxes/train",
- "/demo/src/sandboxes/svg-filter",
- "/demo/src/sandboxes/css-keyframes",
- "/demo/src/sandboxes/tree",
"/demo/src/sandboxes/notification-hub",
- "/demo/src/sandboxes/rocket-decay",
"/demo/src/sandboxes/parallax",
- "/demo/src/sandboxes/parallax-vert"
+ "/demo/src/sandboxes/parallax-vert",
+ "/demo/src/sandboxes/rocket-decay",
+ "/demo/src/sandboxes/simple-transition",
+ "/demo/src/sandboxes/svg-filter",
+ "/demo/src/sandboxes/slide",
+ "/demo/src/sandboxes/train",
+ "/demo/src/sandboxes/tree",
+ "/demo/src/sandboxes/viewpager"
],
"node": "14"
}
diff --git a/demo/src/App.jsx b/demo/src/App.jsx
index 0196e605fa..50919f0eae 100644
--- a/demo/src/App.jsx
+++ b/demo/src/App.jsx
@@ -2,54 +2,69 @@ import React from 'react'
import { Link, Route } from 'wouter'
import styles from './styles.module.css'
-import GooBlobs from './sandboxes/goo-blobs/src/App'
+import AnimatingAuto from './sandboxes/animating-auto/src/App'
+
import Card from './sandboxes/card/src/App'
-import FlipCard from './sandboxes/flip-card/src/App'
-import Slide from './sandboxes/slide/src/App'
-import DraggableList from './sandboxes/draggable-list/src/App'
import CardsStack from './sandboxes/cards-stack/src/App'
-import Viewpager from './sandboxes/viewpager/src/App'
-import SimpleTransition from './sandboxes/simple-transition/src/App'
+import Chain from './sandboxes/chain/src/App'
+import CssKeyframes from './sandboxes/css-keyframes/src/App'
+
+import DecayRocket from './sandboxes/rocket-decay/src/App'
+import DraggableList from './sandboxes/draggable-list/src/App'
+
+import ExitBeforeEnter from './sandboxes/exit-before-enter/src/App'
+
+import FlipCard from './sandboxes/flip-card/src/App'
+
+import GooBlobs from './sandboxes/goo-blobs/src/App'
+
import ImageFade from './sandboxes/image-fade/src/App'
+
import ListReordering from './sandboxes/list-reordering/src/App'
-import Chain from './sandboxes/chain/src/App'
+
import Masonry from './sandboxes/masonry/src/App'
-import AnimatingAuto from './sandboxes/animating-auto/src/App'
import MultiStageTransition from './sandboxes/multistage-transition/src/App'
-import Trail from './sandboxes/trail/src/App'
-import SvgFilter from './sandboxes/svg-filter/src/App'
-import CssKeyframes from './sandboxes/css-keyframes/src/App'
+
import NotificationHub from './sandboxes/notification-hub/src/App'
-import Tree from './sandboxes/tree/src/App'
-import DecayRocket from './sandboxes/rocket-decay/src/App'
+
import Parallax from './sandboxes/parallax/src/App'
import ParallaxVert from './sandboxes/parallax-vert/src/App'
import ParallaxSticky from './sandboxes/parallax-sticky/src/App'
+import SimpleTransition from './sandboxes/simple-transition/src/App'
+import Slide from './sandboxes/slide/src/App'
+import SvgFilter from './sandboxes/svg-filter/src/App'
+
+import Trail from './sandboxes/trail/src/App'
+import Tree from './sandboxes/tree/src/App'
+
+import Viewpager from './sandboxes/viewpager/src/App'
+
const links = {
+ 'animating-auto': AnimatingAuto,
card: Card,
+ 'cards-stack': CardsStack,
chain: Chain,
+ 'css-keyframes': CssKeyframes,
+ 'decay-rocket': DecayRocket,
+ 'draggable-list': DraggableList,
+ 'exit-before-enter': ExitBeforeEnter,
'flip-card': FlipCard,
'goo-blobs': GooBlobs,
- slide: Slide,
- 'draggable-list': DraggableList,
- 'cards-stack': CardsStack,
- viewpager: Viewpager,
- 'simple-transition': SimpleTransition,
'image-fade': ImageFade,
'list-reordering': ListReordering,
masonry: Masonry,
- 'animating-auto': AnimatingAuto,
'multistage-transition': MultiStageTransition,
- trail: Trail,
- 'svg-filter': SvgFilter,
- 'css-keyframes': CssKeyframes,
'notification-hub': NotificationHub,
- tree: Tree,
- 'decay-rocket': DecayRocket,
parallax: Parallax,
'parallax-vert': ParallaxVert,
'parallax-sticky': ParallaxSticky,
+ 'simple-transition': SimpleTransition,
+ slide: Slide,
+ 'svg-filter': SvgFilter,
+ trail: Trail,
+ tree: Tree,
+ viewpager: Viewpager,
}
const Example = ({ link }) => {
diff --git a/demo/src/sandboxes/exit-before-enter/package.json b/demo/src/sandboxes/exit-before-enter/package.json
new file mode 100644
index 0000000000..c093c943aa
--- /dev/null
+++ b/demo/src/sandboxes/exit-before-enter/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "spring-exit-before-enter",
+ "version": "1.0.0",
+ "main": "src/index.tsx",
+ "dependencies": {
+ "@react-spring/web": "*",
+ "react": "^17.0.1",
+ "react-dom": "^17.0.1",
+ "react-scripts": "4.0.3"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test --env=jsdom",
+ "eject": "react-scripts eject"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ],
+ "bic": false,
+ "devDependencies": {
+ "@types/react": "^17.0.2",
+ "@types/react-dom": "^17.0.1",
+ "typescript": "^4.2.3"
+ }
+}
diff --git a/demo/src/sandboxes/exit-before-enter/public/img_01.jpg b/demo/src/sandboxes/exit-before-enter/public/img_01.jpg
new file mode 100644
index 0000000000..67be6a5615
Binary files /dev/null and b/demo/src/sandboxes/exit-before-enter/public/img_01.jpg differ
diff --git a/demo/src/sandboxes/exit-before-enter/public/img_02.png b/demo/src/sandboxes/exit-before-enter/public/img_02.png
new file mode 100644
index 0000000000..6bb6559eb8
Binary files /dev/null and b/demo/src/sandboxes/exit-before-enter/public/img_02.png differ
diff --git a/demo/src/sandboxes/exit-before-enter/public/img_03.jpeg b/demo/src/sandboxes/exit-before-enter/public/img_03.jpeg
new file mode 100644
index 0000000000..fca574ee77
Binary files /dev/null and b/demo/src/sandboxes/exit-before-enter/public/img_03.jpeg differ
diff --git a/demo/src/sandboxes/exit-before-enter/public/index.html b/demo/src/sandboxes/exit-before-enter/public/index.html
new file mode 100644
index 0000000000..2a1959d610
--- /dev/null
+++ b/demo/src/sandboxes/exit-before-enter/public/index.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+ React Spring Sandbox
+
+
+
+
+
+
+
+
diff --git a/demo/src/sandboxes/exit-before-enter/src/App.tsx b/demo/src/sandboxes/exit-before-enter/src/App.tsx
new file mode 100644
index 0000000000..6f379072e5
--- /dev/null
+++ b/demo/src/sandboxes/exit-before-enter/src/App.tsx
@@ -0,0 +1,81 @@
+import React, { useEffect, useLayoutEffect, useState } from 'react'
+import {
+ useSpringRef,
+ animated,
+ useSpring,
+ useTransition,
+ config,
+} from '@react-spring/web'
+
+import styles from './styles.module.css'
+
+import IMG_01 from '../public/img_01.jpg'
+import IMG_02 from '../public/img_02.png'
+import IMG_03 from '../public/img_03.jpeg'
+
+const IMAGES = [IMG_01, IMG_02, IMG_03]
+
+export default function App() {
+ const [activeIndex, setActiveIndex] = useState(0)
+ const springApi = useSpringRef()
+
+ const transitions = useTransition(activeIndex, {
+ from: {
+ clipPath: 'polygon(0% 0%, 0% 100%, 0% 100%, 0% 0%)',
+ },
+ enter: {
+ clipPath: 'polygon(0% 0%, 0% 100%, 100% 100%, 100% 0%)',
+ },
+ leave: {
+ clipPath: 'polygon(100% 0%, 100% 100%, 100% 100%, 100% 0%)',
+ },
+ onRest: (_springs, _ctrl, item) => {
+ if (activeIndex === item) {
+ setActiveIndex(activeIndex === IMAGES.length - 1 ? 0 : activeIndex + 1)
+ }
+ },
+ exitBeforeEnter: true,
+ config: {
+ duration: 4000,
+ },
+ ref: springApi,
+ })
+
+ const springs = useSpring({
+ from: {
+ strokeDashoffset: 120,
+ },
+ to: {
+ strokeDashoffset: 0,
+ },
+ config: {
+ duration: 8000,
+ },
+ loop: true,
+ ref: springApi,
+ })
+
+ useLayoutEffect(() => {
+ springApi.start()
+ }, [activeIndex])
+
+ return (
+
+ {transitions((springs, item) => (
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/demo/src/sandboxes/exit-before-enter/src/index.css b/demo/src/sandboxes/exit-before-enter/src/index.css
new file mode 100644
index 0000000000..9866fc8c9e
--- /dev/null
+++ b/demo/src/sandboxes/exit-before-enter/src/index.css
@@ -0,0 +1,30 @@
+html,
+body,
+#root {
+ height: 100%;
+ width: 100%;
+}
+
+body {
+ font-family: system-ui;
+ margin: 0;
+}
+
+*,
+*:after,
+*:before {
+ box-sizing: border-box;
+}
+
+.flex {
+ display: flex;
+ align-items: center;
+}
+
+.flex.fill {
+ height: 100%;
+}
+
+.flex.center {
+ justify-content: center;
+}
diff --git a/demo/src/sandboxes/exit-before-enter/src/index.tsx b/demo/src/sandboxes/exit-before-enter/src/index.tsx
new file mode 100644
index 0000000000..a9143cbcdf
--- /dev/null
+++ b/demo/src/sandboxes/exit-before-enter/src/index.tsx
@@ -0,0 +1,12 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import App from './App'
+import './index.css'
+
+const rootElement = document.getElementById('root')
+ReactDOM.render(
+
+
+ ,
+ rootElement
+)
diff --git a/demo/src/sandboxes/exit-before-enter/src/styles.module.css b/demo/src/sandboxes/exit-before-enter/src/styles.module.css
new file mode 100644
index 0000000000..7d201231f1
--- /dev/null
+++ b/demo/src/sandboxes/exit-before-enter/src/styles.module.css
@@ -0,0 +1,59 @@
+.container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+}
+
+.img__container {
+ overflow: hidden;
+ position: relative;
+ width: 80vw;
+}
+
+.img__container::before {
+ display: block;
+ content: '';
+ width: 100%;
+ padding-top: 56.25%;
+}
+
+.img__container > img {
+ position: absolute;
+ min-width: 100%;
+ max-width: 100%;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+}
+
+.ticker {
+ position: absolute;
+ height: 4rem;
+ width: 4rem;
+ bottom: 2rem;
+ right: 2rem;
+}
+
+.ticker > * {
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.ticker > div {
+ opacity: 0.2;
+ border: solid 3px black;
+}
+
+.ticker > svg {
+ fill: none;
+ stroke: black;
+ stroke-width: 3;
+ stroke-dasharray: 120;
+ stroke-dashoffset: 120;
+}
diff --git a/demo/src/sandboxes/image-fade/src/App.tsx b/demo/src/sandboxes/image-fade/src/App.tsx
index 0326a0c2eb..e1588609d2 100644
--- a/demo/src/sandboxes/image-fade/src/App.tsx
+++ b/demo/src/sandboxes/image-fade/src/App.tsx
@@ -17,11 +17,13 @@ export default function App() {
enter: { opacity: 1 },
leave: { opacity: 0 },
config: { duration: 3000 },
+ onRest: (_a, _b, item) => {
+ if (index === item) {
+ set(state => (state + 1) % slides.length)
+ }
+ },
+ exitBeforeEnter: true,
})
- useEffect(() => {
- const t = setInterval(() => set(state => (state + 1) % slides.length), 4000)
- return () => clearTimeout(t)
- }, [])
return (
{transitions((style, i) => (
diff --git a/demo/src/styles.module.css b/demo/src/styles.module.css
index f68ab9ad06..0a6a5fb8b9 100644
--- a/demo/src/styles.module.css
+++ b/demo/src/styles.module.css
@@ -1,8 +1,12 @@
.page {
margin: 0 auto;
max-width: 720px;
- padding: 20vh 16px 0;
+ padding: 40px 16px 0;
min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+ flex-direction: column;
}
.back {
@@ -21,10 +25,13 @@
}
.link {
+ display: inline-block;
color: inherit;
}
.linkList {
display: grid;
+ justify-content: flex-start;
gap: 10px;
+ margin-bottom: 80px;
}
diff --git a/docs/src/pages/basics.mdx b/docs/src/pages/basics.mdx
index b87b4b4639..256e36adeb 100644
--- a/docs/src/pages/basics.mdx
+++ b/docs/src/pages/basics.mdx
@@ -388,29 +388,26 @@ For performance reasons, the attributes of animated DOM elements are updated dir
For example:
```jsx
-const MyAnimatedComponent = animated(({ value }) => (
-
{value}
-))
+const MyAnimatedComponent = animated(({ value }) =>
{value}
)
-const MyAnimatedComponentWithRefForwarding = animated(forwardRef(({ value }, ref) => (
-
{value}
-)))
+const MyAnimatedComponentWithRefForwarding = animated(
+ forwardRef(({ value }, ref) =>
{value}
)
+)
const MyComponentWithSpring = () => {
const { value } = useSpring({
from: { value: 0 },
to: { value: 1 },
- });
-
+ })
+
return (
<>
/* This component will automatically update as "value" changes */
-
/* This component will not automatically update as "value" changes */
>
- );
+ )
}
```
diff --git a/packages/core/src/hooks/useTransition.test.tsx b/packages/core/src/hooks/useTransition.test.tsx
index 1db2740210..c6789ae71c 100644
--- a/packages/core/src/hooks/useTransition.test.tsx
+++ b/packages/core/src/hooks/useTransition.test.tsx
@@ -166,6 +166,31 @@ describe('useTransition', () => {
return null
})
})
+
+ it('should allow items to leave before entering new items when exitBeforeEnter is true', async () => {
+ const props = {
+ from: { t: 0 },
+ enter: { t: 1 },
+ leave: { t: 1 },
+ exitBeforeEnter: true,
+ }
+
+ update(0, props)
+
+ expect(rendered).toEqual([0])
+
+ global.mockRaf.step()
+
+ update(1, props)
+
+ global.mockRaf.step()
+
+ expect(rendered).toEqual([0])
+
+ await global.advanceUntilIdle()
+
+ expect(rendered).toEqual([1])
+ })
})
function createUpdater(
diff --git a/packages/core/src/hooks/useTransition.tsx b/packages/core/src/hooks/useTransition.tsx
index 42548443ca..9fe263abdb 100644
--- a/packages/core/src/hooks/useTransition.tsx
+++ b/packages/core/src/hooks/useTransition.tsx
@@ -78,6 +78,7 @@ export function useTransition(
sort,
trail = 0,
expires = true,
+ exitBeforeEnter = false,
onDestroyed,
ref: propsRef,
config: propsConfig,
@@ -96,21 +97,21 @@ export function useTransition(
// The "onRest" callbacks need a ref to the latest transitions.
const usedTransitions = useRef
(null)
const prevTransitions = reset ? null : usedTransitions.current
+
useLayoutEffect(() => {
usedTransitions.current = transitions
})
// Destroy all transitions on dismount.
- useOnce(
- () => () =>
- each(usedTransitions.current!, t => {
- if (t.expired) {
- clearTimeout(t.expirationId!)
- }
- detachRefs(t.ctrl, ref)
- t.ctrl.stop(true)
- })
- )
+ useOnce(() => () => {
+ each(usedTransitions.current!, t => {
+ if (t.expired) {
+ clearTimeout(t.expirationId!)
+ }
+ detachRefs(t.ctrl, ref)
+ t.ctrl.stop(true)
+ })
+ })
// Keys help with reusing transitions between renders.
// The `key` prop can be undefined (which means the items themselves are used
@@ -185,6 +186,9 @@ export function useTransition(
const defaultProps = getDefaultProps(props)
// Generate changes to apply in useEffect.
const changes = new Map()
+ const exitingTransitions = useRef(new Map())
+
+ const forceChange = useRef(false)
each(transitions, (t, i) => {
const key = t.key
const prevPhase = t.phase
@@ -300,13 +304,35 @@ export function useTransition(
}
// Force update once idle and expired items exist.
if (idle && transitions.some(t => t.expired)) {
+ /**
+ * Remove the exited transition from the list
+ * this may not exist but we'll try anyway.
+ */
+ exitingTransitions.current.delete(t)
+
+ if (exitBeforeEnter) {
+ /**
+ * If we have exitBeforeEnter == true
+ * we need to force the animation to start
+ */
+ forceChange.current = true
+ }
+
forceUpdate()
}
}
}
const springs = getSprings(t.ctrl, payload)
- changes.set(t, { phase, springs, payload })
+
+ /**
+ * Make a separate map for the exiting changes and "regular" changes
+ */
+ if (phase === TransitionPhase.LEAVE && exitBeforeEnter) {
+ exitingTransitions.current.set(t, { phase, springs, payload })
+ } else {
+ changes.set(t, { phase, springs, payload })
+ }
})
// The prop overrides from an ancestor.
@@ -316,39 +342,68 @@ export function useTransition(
// Merge the context into each transition.
useLayoutEffect(() => {
- if (hasContext)
+ if (hasContext) {
each(transitions, t => {
t.ctrl.start({ default: context })
})
+ }
}, [context])
+ each(changes, (_, t) => {
+ /**
+ * If we have children to exit because exitBeforeEnter is
+ * set to true, we remove the transitions so they go to back
+ * to their initial state.
+ */
+ if (exitingTransitions.current.size) {
+ const ind = transitions.findIndex(state => state.key === t.key)
+ transitions.splice(ind, 1)
+ }
+ })
+
useLayoutEffect(
() => {
- each(changes, ({ phase, payload }, t) => {
- const { ctrl } = t
- t.phase = phase
-
- // Attach the controller to our local ref.
- ref?.add(ctrl)
-
- // Merge the context into new items.
- if (hasContext && phase == TransitionPhase.ENTER) {
- ctrl.start({ default: context })
- }
-
- if (payload) {
- // Update the injected ref if needed.
- replaceRef(ctrl, payload.ref)
+ /*
+ * if exitingTransitions.current has a size it means we're exiting before enter
+ * so we want to map through those and fire those first.
+ */
+ each(
+ exitingTransitions.current.size ? exitingTransitions.current : changes,
+ ({ phase, payload }, t) => {
+ const { ctrl } = t
+
+ t.phase = phase
+
+ // Attach the controller to our local ref.
+ ref?.add(ctrl)
+
+ // Merge the context into new items.
+ if (hasContext && phase == TransitionPhase.ENTER) {
+ ctrl.start({ default: context })
+ }
- // When an injected ref exists, the update is postponed
- // until the ref has its `start` method called.
- if (ctrl.ref) {
- ctrl.update(payload)
- } else {
- ctrl.start(payload)
+ if (payload) {
+ // Update the injected ref if needed.
+ replaceRef(ctrl, payload.ref)
+
+ /**
+ * When an injected ref exists, the update is postponed
+ * until the ref has its `start` method called.
+ * Unless we have exitBeforeEnter in which case will skip
+ * to enter the new animation straight away as if they "overlapped"
+ */
+ if (ctrl.ref && !forceChange.current) {
+ ctrl.update(payload)
+ } else {
+ ctrl.start(payload)
+
+ if (forceChange.current) {
+ forceChange.current = false
+ }
+ }
}
}
- })
+ )
},
reset ? void 0 : deps
)
diff --git a/packages/core/src/types/transition.ts b/packages/core/src/types/transition.ts
index 6a4a4b2af3..2977b334f5 100644
--- a/packages/core/src/types/transition.ts
+++ b/packages/core/src/types/transition.ts
@@ -63,6 +63,7 @@ export type UseTransitionProps- = Merge<
keys?: ItemKeys
-
sort?: (a: Item, b: Item) => number
trail?: number
+ exitBeforeEnter?: boolean
/**
* When `true` or `<= 0`, each item is unmounted immediately after its
* `leave` animation is finished.
diff --git a/yarn.lock b/yarn.lock
index 4344ae0fed..68179638f3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2480,6 +2480,99 @@
pirates "^4.0.1"
source-map-support "^0.5.16"
+"@react-spring/animated@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/animated/-/animated-9.3.1.tgz#ffc4706121e8406efeaeacb407b42b5022943b46"
+ integrity sha512-23YaERZ++BwZ8F8PxPFqrpOwp/JZun1Pj6aHZtPAU42j5LycBRasT9XMw7Eyr7zNFhT+rl3R3wFfd4WX6Ax+UA==
+ dependencies:
+ "@react-spring/shared" "~9.3.0"
+ "@react-spring/types" "~9.3.0"
+
+"@react-spring/core@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/core/-/core-9.3.1.tgz#b98e1dca1eb4871dec75fdab350327e8a5222865"
+ integrity sha512-8rmfmEHLHGtF1CUiXRn64YJqsXNxv2cGX8oNnBnsuoE33c48Zc34t2VIMB4R9q5zwIUCvDBGfiEenA8ZAPxqOQ==
+ dependencies:
+ "@react-spring/animated" "~9.3.0"
+ "@react-spring/shared" "~9.3.0"
+ "@react-spring/types" "~9.3.0"
+
+"@react-spring/konva@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/konva/-/konva-9.3.1.tgz#7a915c1c912a81dc27d1a1bc31eb47c0a2a0c643"
+ integrity sha512-woG2DeDcUlz5hB8g9pA/tyUWU6dMrAzyUsNiBWVCyI9UqKA7CUKjz+ODOUi+hS++3Kz7kZSr3u0zzHHfxvoTPQ==
+ dependencies:
+ "@react-spring/animated" "~9.3.0"
+ "@react-spring/core" "~9.3.0"
+ "@react-spring/shared" "~9.3.0"
+ "@react-spring/types" "~9.3.0"
+
+"@react-spring/native@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/native/-/native-9.3.1.tgz#e1ac9d04d833b8d97e2d63630c4204f71915a29a"
+ integrity sha512-NAC1wHIUvy1umCVQRxcS+31Dmr9NszBY06sHy3jR8/HVuKNtaDUARVF3AYL/HfbIy4m6yR3tcIkM2NQ0SO+rZA==
+ dependencies:
+ "@react-spring/animated" "~9.3.0"
+ "@react-spring/core" "~9.3.0"
+ "@react-spring/shared" "~9.3.0"
+ "@react-spring/types" "~9.3.0"
+
+"@react-spring/parallax@*":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/parallax/-/parallax-9.3.1.tgz#714252f93702c621e484e98b31e402c44b476443"
+ integrity sha512-c1WUluGg3OChThxzOzXFi51tpOHINgl4cT2WtN/o4TqOmVJaP6aTqiLqQxsQfdVfNhUQH+6TEICJahRHZ43jUQ==
+ dependencies:
+ "@react-spring/shared" "~9.3.0"
+ "@react-spring/web" "~9.3.0"
+
+"@react-spring/rafz@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.3.1.tgz#8dd6a598ffea487252b75d05d199e4aca5ea9d5e"
+ integrity sha512-fEBMCarGVl+/2kdO+g6Zig4F+3ymwmcGN8S71gb1c7Cbbxb87kviPz8EhshfIHoiLeJPGlqwcuGbxNmZbBamvA==
+
+"@react-spring/shared@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.3.1.tgz#e7f22a4b8f5fea4491fa6a24c108db5abd19ddba"
+ integrity sha512-jhPpxzURGo6Nty90ex1lkxmZae7w/VAbnGmb/nXcYoZwSoNR+W2aAd00iXsh2ZGz6MgoJOsc495JeG3uC7Am8A==
+ dependencies:
+ "@react-spring/rafz" "~9.3.0"
+ "@react-spring/types" "~9.3.0"
+
+"@react-spring/three@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/three/-/three-9.3.1.tgz#91a50851639c5a88fed8f76b4e25ff388d2b24e7"
+ integrity sha512-40iRIX2DrY+a81hIliOog6TMg/ZAtHGeZr95r0vKAsl+iX1g9Hs8XCS4wTeQIUgydZpbpShk/JL6mkcstEfBdw==
+ dependencies:
+ "@react-spring/animated" "~9.3.0"
+ "@react-spring/core" "~9.3.0"
+ "@react-spring/shared" "~9.3.0"
+ "@react-spring/types" "~9.3.0"
+
+"@react-spring/types@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.3.1.tgz#20f392ecad15a1ea6c0865ffe86ca5016c05a278"
+ integrity sha512-W/YMJMX35XgGGzX0gKORBTwnvQ+1loDOFN3XlZkW5fgpEY+7VkRUpPyqPWXQr3n6lHrsLmHIGdpznqZi54ACTQ==
+
+"@react-spring/web@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/web/-/web-9.3.1.tgz#5b377ba7ad52e746c2b59e2738c021de3f219d0b"
+ integrity sha512-sisZIgFGva/Z+xKWPSfXpukF0AP3kR9ALTxlHL87fVotMUCJX5vtH/YlVcywToEFwTHKt3MpI5Wy2M+vgVEeaw==
+ dependencies:
+ "@react-spring/animated" "~9.3.0"
+ "@react-spring/core" "~9.3.0"
+ "@react-spring/shared" "~9.3.0"
+ "@react-spring/types" "~9.3.0"
+
+"@react-spring/zdog@~9.3.0":
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/@react-spring/zdog/-/zdog-9.3.1.tgz#70fccd80c35248217de6252c4c2ffca16b4cff2f"
+ integrity sha512-QflA/fII9zWe9CSOA8QGSLMjUwyrtD1TX6YVzUSn/nlr2f7PlZPijdpdu9Cvdirgss472cUS7cRIUfll0TepqA==
+ dependencies:
+ "@react-spring/animated" "~9.3.0"
+ "@react-spring/core" "~9.3.0"
+ "@react-spring/shared" "~9.3.0"
+ "@react-spring/types" "~9.3.0"
+
"@react-three/fiber@^6.2.2":
version "6.2.2"
resolved "https://registry.yarnpkg.com/@react-three/fiber/-/fiber-6.2.2.tgz#bced876201cb136e91d5b0891f31e5c740e1537c"
@@ -10253,6 +10346,18 @@ react-simple-code-editor@^0.11.0:
resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.11.0.tgz#bb57c7c29b570f2ab229872599eac184f5bc673c"
integrity sha512-xGfX7wAzspl113ocfKQAR8lWPhavGWHL3xSzNLeseDRHysT+jzRBi/ExdUqevSMos+7ZtdfeuBOXtgk9HTwsrw==
+react-spring@*:
+ version "9.3.2"
+ resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-9.3.2.tgz#1456ef1ab1a3997c6c39693a9fd18cfd46c92930"
+ integrity sha512-LMFgjvTJVg2ltthzgJcZ7siOdRig7L8wJZIHrc7p6ss4AaJ446xHzm9L2ZQha1ZC9QVY8/e6XfLhMTFAG6dtjw==
+ dependencies:
+ "@react-spring/core" "~9.3.0"
+ "@react-spring/konva" "~9.3.0"
+ "@react-spring/native" "~9.3.0"
+ "@react-spring/three" "~9.3.0"
+ "@react-spring/web" "~9.3.0"
+ "@react-spring/zdog" "~9.3.0"
+
react-three-fiber@0.0.0-deprecated:
version "0.0.0-deprecated"
resolved "https://registry.yarnpkg.com/react-three-fiber/-/react-three-fiber-0.0.0-deprecated.tgz#c737242487d824cf9520307308b7e4c4071a278f"