import { createContext, useContext, useEffect, useState, useCallback, } from "react"; import { createPortal } from "react-dom"; import { motion, AnimatePresence } from "framer-motion"; import { IoMdArrowRoundBack } from "react-icons/io"; import { cn } from "@/lib/utils"; import { isPWA } from "@/utils/isPWA"; import { Button } from "@/components/ui/button"; import { useTranslation } from "react-i18next"; import { useLocation } from "react-router-dom"; const MobilePageContext = createContext<{ open: boolean; onOpenChange: (open: boolean) => void; } | null>(null); type MobilePageProps = { children: React.ReactNode; open?: boolean; onOpenChange?: (open: boolean) => void; }; export function MobilePage({ children, open: controlledOpen, onOpenChange, }: MobilePageProps) { const [uncontrolledOpen, setUncontrolledOpen] = useState(false); const location = useLocation(); const open = controlledOpen ?? uncontrolledOpen; const setOpen = useCallback( (value: boolean) => { if (onOpenChange) { onOpenChange(value); } else { setUncontrolledOpen(value); } }, [onOpenChange, setUncontrolledOpen], ); useEffect(() => { let isActive = true; if (open && isActive) { window.history.pushState({ isMobilePage: true }, "", location.pathname); } const handlePopState = (event: PopStateEvent) => { if (open && isActive) { event.preventDefault(); setOpen(false); // Delay replaceState to ensure state updates are processed setTimeout(() => { if (isActive) { window.history.replaceState(null, "", location.pathname); } }, 0); } }; window.addEventListener("popstate", handlePopState); return () => { isActive = false; window.removeEventListener("popstate", handlePopState); }; }, [open, setOpen, location.pathname]); return ( {children} ); } type MobilePageTriggerProps = React.HTMLAttributes; export function MobilePageTrigger({ children, ...props }: MobilePageTriggerProps) { const context = useContext(MobilePageContext); if (!context) throw new Error("MobilePageTrigger must be used within MobilePage"); return (
context.onOpenChange(true)} {...props}> {children}
); } type MobilePagePortalProps = { children: React.ReactNode; container?: HTMLElement; }; export function MobilePagePortal({ children, container, }: MobilePagePortalProps) { const [mounted, setMounted] = useState(false); useEffect(() => { setMounted(true); return () => setMounted(false); }, []); if (!mounted) return null; return createPortal(children, container || document.body); } type MobilePageContentProps = { children: React.ReactNode; className?: string; }; export function MobilePageContent({ children, className, }: MobilePageContentProps) { const context = useContext(MobilePageContext); if (!context) throw new Error("MobilePageContent must be used within MobilePage"); const [isVisible, setIsVisible] = useState(context.open); useEffect(() => { if (context.open) { setIsVisible(true); } }, [context.open]); const handleAnimationComplete = () => { if (!context.open) { setIsVisible(false); } }; return ( {isVisible && ( {children} )} ); } interface MobilePageHeaderProps extends React.HTMLAttributes { onClose?: () => void; actions?: React.ReactNode; } export function MobilePageHeader({ children, className, onClose, actions, ...props }: MobilePageHeaderProps) { const { t } = useTranslation(["common"]); const context = useContext(MobilePageContext); if (!context) throw new Error("MobilePageHeader must be used within MobilePage"); const handleClose = () => { if (onClose) { onClose(); } else { context.onOpenChange(false); } }; return (
{children}
{actions && (
{actions}
)}
); } type MobilePageTitleProps = React.HTMLAttributes; export function MobilePageTitle({ className, ...props }: MobilePageTitleProps) { return

; } type MobilePageDescriptionProps = React.HTMLAttributes; export function MobilePageDescription({ className, ...props }: MobilePageDescriptionProps) { return (

); }