import React, {
unstable_addTransitionType as addTransitionType,
unstable_ViewTransition as ViewTransition,
unstable_Activity as Activity,
useLayoutEffect,
useEffect,
useState,
useId,
useOptimistic,
startTransition,
Suspense,
} from 'react';
import {createPortal} from 'react-dom';
import SwipeRecognizer from './SwipeRecognizer.js';
import './Page.css';
import transitions from './Transitions.module.css';
import NestedReveal from './NestedReveal.js';
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const a = (
<div key="a">
<ViewTransition>
<div>a</div>
</ViewTransition>
</div>
);
const b = (
<div key="b">
<ViewTransition>
<div>b</div>
</ViewTransition>
</div>
);
function Component() {
return (
<ViewTransition
default={
transitions['enter-slide-right'] + ' ' + transitions['exit-slide-left']
}>
<p className="roboto-font">Slide In from Left, Slide Out to Right</p>
<p>
<img
src="https://react.dev/_next/image?url=%2Fimages%2Fteam%2Fsebmarkbage.jpg&w=3840&q=75"
width="300"
/>
</p>
</ViewTransition>
);
}
function Id() {
return <span id={useId()} />;
}
let wait;
function Suspend() {
if (!wait) wait = sleep(500);
return React.use(wait);
}
export default function Page({url, navigate}) {
const [renderedUrl, optimisticNavigate] = useOptimistic(
url,
(state, direction) => {
return direction === 'left' ? '/?a' : '/?b';
}
);
const show = renderedUrl === '/?b';
function onTransition(viewTransition, types) {
const keyframes = [
{rotate: '0deg', transformOrigin: '30px 8px'},
{rotate: '360deg', transformOrigin: '30px 8px'},
];
viewTransition.old.animate(keyframes, 250);
viewTransition.new.animate(keyframes, 250);
}
function swipeAction() {
navigate(show ? '/?a' : '/?b');
}
const [counter, setCounter] = useState(0);
useEffect(() => {
const timer = setInterval(() => setCounter(c => c + 1), 1000);
return () => clearInterval(timer);
}, []);
useLayoutEffect(() => {
}, [show]);
const [showModal, setShowModal] = useState(false);
const portal = showModal ? (
createPortal(
<div className="portal">
Portal: {!show ? 'A' : 'B'}
<ViewTransition>
<div>{!show ? 'A' : 'B'}</div>
</ViewTransition>
</div>,
document.body
)
) : (
<button
onClick={() =>
startTransition(async () => {
await sleep(2000);
setShowModal(true);
})
}>
Show Modal
</button>
);
const exclamation = (
<ViewTransition name="exclamation" onShare={onTransition}>
<span>
<div>!</div>
</span>
</ViewTransition>
);
return (
<div className="swipe-recognizer">
<SwipeRecognizer
action={swipeAction}
gesture={direction => {
addTransitionType(
direction === 'left' ? 'navigation-forward' : 'navigation-back'
);
optimisticNavigate(direction);
}}
direction={show ? 'left' : 'right'}>
<button
className="button"
onClick={() => {
navigate(url === '/?b' ? '/?a' : '/?b');
}}>
{url === '/?b' ? 'Goto A' : 'Goto B'}
</button>
<ViewTransition default="none">
<div>
<ViewTransition>
<div>
<ViewTransition default={transitions['slide-on-nav']}>
<h1>{!show ? 'A' : 'B' + counter}</h1>
</ViewTransition>
</div>
</ViewTransition>
<ViewTransition
default={{
'navigation-back': transitions['slide-right'],
'navigation-forward': transitions['slide-left'],
}}>
<h1>{!show ? 'A' + counter : 'B'}</h1>
</ViewTransition>
{show ? (
<div>
{a}
{b}
</div>
) : (
<div>
{b}
{a}
</div>
)}
<ViewTransition>
{show ? (
<div>hello{exclamation}</div>
) : (
<section>Loading</section>
)}
</ViewTransition>
<p>
<Id />
</p>
{show ? null : (
<ViewTransition>
<div>world{exclamation}</div>
</ViewTransition>
)}
<Activity mode={show ? 'visible' : 'hidden'}>
<ViewTransition>
<div>!!</div>
</ViewTransition>
</Activity>
<Suspense
fallback={
<ViewTransition>
<div>
<ViewTransition name="shared-reveal">
<h2>█████</h2>
</ViewTransition>
<p>████</p>
<p>███████</p>
<p>████</p>
<p>██</p>
<p>██████</p>
<p>███</p>
<p>████</p>
</div>
</ViewTransition>
}>
<ViewTransition>
<div>
<p>these</p>
<p>rows</p>
<ViewTransition name="shared-reveal">
<h2>exist</h2>
</ViewTransition>
<p>to</p>
<p>test</p>
<p>scrolling</p>
<p>content</p>
<p>out</p>
<p>of</p>
{portal}
<p>the</p>
<p>viewport</p>
<Suspend />
</div>
</ViewTransition>
</Suspense>
{show ? <Component /> : null}
</div>
</ViewTransition>
</SwipeRecognizer>
<NestedReveal />
</div>
);
}