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"