import React from 'react'; import { AnimatePresence, motion, useReducedMotion } from 'framer-motion'; import { Outlet, useLocation, useNavigationType } from 'react-router-dom'; const TAB_SECTIONS = new Set(['home', 'tasks', 'achievements', 'gallery']); export function getTabKey(pathname: string): string | null { const match = pathname.match(/^\/e\/[^/]+(?:\/([^/]+))?$/); if (!match) { return null; } const section = match[1]; if (!section) { return 'home'; } return TAB_SECTIONS.has(section) ? section : null; } export function getTransitionKind(prevPath: string, nextPath: string): 'tab' | 'stack' { const prevTab = getTabKey(prevPath); const nextTab = getTabKey(nextPath); if (prevTab && nextTab && prevTab !== nextTab) { return 'tab'; } return 'stack'; } export function isTransitionDisabled(pathname: string): boolean { if (pathname.startsWith('/share/')) { return true; } return /^\/e\/[^/]+\/upload(?:\/|$)/.test(pathname); } export default function RouteTransition({ children }: { children?: React.ReactNode }) { const location = useLocation(); const navigationType = useNavigationType(); const prefersReducedMotion = useReducedMotion(); const prevPathRef = React.useRef(location.pathname); const prevPath = prevPathRef.current; const direction = navigationType === 'POP' ? 'back' : 'forward'; const kind = getTransitionKind(prevPath, location.pathname); const disableTransitions = prefersReducedMotion || isTransitionDisabled(prevPath) || isTransitionDisabled(location.pathname); React.useEffect(() => { prevPathRef.current = location.pathname; }, [location.pathname]); const content = children ?? ; if (disableTransitions) { return <>{content}; } const stackVariants = { enter: ({ direction }: { direction: 'forward' | 'back' }) => ({ x: direction === 'back' ? -28 : 28, opacity: 0, }), center: { x: 0, opacity: 1 }, exit: ({ direction }: { direction: 'forward' | 'back' }) => ({ x: direction === 'back' ? 28 : -28, opacity: 0, }), }; const tabVariants = { enter: { opacity: 0, scale: 0.985 }, center: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 0.985 }, }; const transition = kind === 'tab' ? { duration: 0.18, ease: [0.22, 0.61, 0.36, 1] } : { duration: 0.24, ease: [0.25, 0.8, 0.25, 1] }; return ( {content} ); }