36 lines
1.0 KiB
TypeScript
36 lines
1.0 KiB
TypeScript
|
|
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||
|
|
|
||
|
|
interface PageTransitionProps {
|
||
|
|
viewKey: string;
|
||
|
|
children: ReactNode;
|
||
|
|
}
|
||
|
|
|
||
|
|
const EXIT_DURATION_MS = 180;
|
||
|
|
|
||
|
|
export default function PageTransition({ viewKey, children }: PageTransitionProps) {
|
||
|
|
const [displayedChildren, setDisplayedChildren] = useState(children);
|
||
|
|
const [phase, setPhase] = useState<"idle" | "exit">("idle");
|
||
|
|
const prevKeyRef = useRef(viewKey);
|
||
|
|
const timerRef = useRef<ReturnType<typeof setTimeout>>();
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (viewKey === prevKeyRef.current) {
|
||
|
|
setDisplayedChildren(children);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
prevKeyRef.current = viewKey;
|
||
|
|
setPhase("exit");
|
||
|
|
timerRef.current = setTimeout(() => {
|
||
|
|
setDisplayedChildren(children);
|
||
|
|
setPhase("idle");
|
||
|
|
}, EXIT_DURATION_MS);
|
||
|
|
return () => clearTimeout(timerRef.current);
|
||
|
|
}, [viewKey, children]);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className={phase === "exit" ? "page-transition-wrap page-motion--exit" : "page-transition-wrap"}>
|
||
|
|
{displayedChildren}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|