beautify: page tranistion / loginform
This commit is contained in:
parent
5709f48dc3
commit
f7205ed8f6
@ -2,34 +2,102 @@
|
|||||||
|
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import React from 'react';
|
import Image from 'next/image';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
const PageTransitionEffect = ({ children }: { children: React.ReactNode }) => {
|
const PageTransitionEffect = ({ children }: { children: React.ReactNode }) => {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
const DELAY_MS = 200;
|
||||||
|
const EXIT_DURATION = 0.7; // slow the fade/slide-out a bit
|
||||||
|
|
||||||
const variants = {
|
const [mounted, setMounted] = useState(false);
|
||||||
hidden: { opacity: 0, x: 0, y: 20 },
|
const [showOverlay, setShowOverlay] = useState(true);
|
||||||
enter: { opacity: 1, x: 0, y: 0 },
|
const [overlayExit, setOverlayExit] = useState(false);
|
||||||
exit: { opacity: 0, x: 0, y: -20 },
|
const delayT = useRef<number | null>(null);
|
||||||
};
|
|
||||||
|
useEffect(() => setMounted(true), []);
|
||||||
|
|
||||||
|
// Exit overlay shortly after route change (200ms)
|
||||||
|
useEffect(() => {
|
||||||
|
setShowOverlay(true);
|
||||||
|
setOverlayExit(false);
|
||||||
|
if (delayT.current) clearTimeout(delayT.current);
|
||||||
|
delayT.current = window.setTimeout(() => setOverlayExit(true), DELAY_MS);
|
||||||
|
return () => {
|
||||||
|
if (delayT.current) clearTimeout(delayT.current);
|
||||||
|
};
|
||||||
|
}, [pathname]);
|
||||||
|
|
||||||
|
// Prevent scroll while overlay is visible
|
||||||
|
useEffect(() => {
|
||||||
|
if (!mounted) return;
|
||||||
|
const prev = document.documentElement.style.overflow;
|
||||||
|
if (showOverlay) document.documentElement.style.overflow = 'hidden';
|
||||||
|
return () => {
|
||||||
|
document.documentElement.style.overflow = prev;
|
||||||
|
};
|
||||||
|
}, [showOverlay, mounted]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence
|
<>
|
||||||
mode="wait"
|
<AnimatePresence mode="wait" onExitComplete={() => window.scrollTo(0, 0)}>
|
||||||
onExitComplete={() => window.scrollTo(0, 0)}
|
<motion.div
|
||||||
>
|
key={pathname}
|
||||||
<motion.div
|
variants={{
|
||||||
key={pathname}
|
hidden: { opacity: 0, x: 0, y: 20 },
|
||||||
variants={variants}
|
enter: { opacity: 1, x: 0, y: 0 },
|
||||||
initial="hidden"
|
exit: { opacity: 0, x: 0, y: -20 },
|
||||||
animate="enter"
|
}}
|
||||||
exit="exit"
|
initial="hidden"
|
||||||
transition={{ type: 'tween', duration: 0.3 }}
|
animate="enter"
|
||||||
className="w-full"
|
exit="exit"
|
||||||
>
|
transition={{ type: 'tween', duration: 0.3 }}
|
||||||
{children}
|
className="w-full"
|
||||||
</motion.div>
|
>
|
||||||
</AnimatePresence>
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{/* Client-only portal overlay with header gradient (no delay, default timing) */}
|
||||||
|
{mounted &&
|
||||||
|
showOverlay &&
|
||||||
|
createPortal(
|
||||||
|
<motion.div
|
||||||
|
initial={false}
|
||||||
|
animate={overlayExit ? { y: '-100%', opacity: 0 } : { y: 0, opacity: 1 }}
|
||||||
|
transition={{ duration: EXIT_DURATION, ease: [0.22, 1, 0.36, 1] }}
|
||||||
|
onAnimationComplete={() => {
|
||||||
|
if (overlayExit) {
|
||||||
|
setShowOverlay(false);
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="fixed inset-0 z-[999999] flex items-center justify-center"
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
'linear-gradient(135deg, #0F1D37 0%, #0A162A 50%, #081224 100%)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center">
|
||||||
|
<Image
|
||||||
|
src="/images/logos/pp_logo_gold_transparent.png"
|
||||||
|
alt="Profit Planet"
|
||||||
|
width={160}
|
||||||
|
height={160}
|
||||||
|
className="w-32 h-32 object-contain"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
role="status"
|
||||||
|
aria-live="polite"
|
||||||
|
className="mt-6 h-10 w-10 rounded-full border-4 border-[#D4AF37] border-t-transparent animate-spin"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</motion.div>,
|
||||||
|
document.body
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -82,7 +82,32 @@ export default function LoginForm() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const isMobile = typeof window !== 'undefined' && window.innerWidth < 768
|
const screenWidth = typeof window !== 'undefined' ? window.innerWidth : 1200
|
||||||
|
const isMobile = screenWidth < 768
|
||||||
|
const isTablet = screenWidth >= 768 && screenWidth <= 1024
|
||||||
|
const isSmallLaptop = screenWidth > 1024 && screenWidth <= 1366
|
||||||
|
|
||||||
|
// Calculate responsive values
|
||||||
|
const getFormWidth = () => {
|
||||||
|
if (isMobile) return '98vw'
|
||||||
|
if (isTablet) return '85vw'
|
||||||
|
if (isSmallLaptop) return '50vw'
|
||||||
|
return '40vw'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFormScale = () => {
|
||||||
|
if (isMobile) return undefined
|
||||||
|
if (isTablet) return 'scale(0.95)'
|
||||||
|
if (isSmallLaptop) return 'scale(0.9)'
|
||||||
|
return 'scale(0.85)'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFormMaxWidth = () => {
|
||||||
|
if (isMobile) return 'none'
|
||||||
|
if (isTablet) return '600px'
|
||||||
|
if (isSmallLaptop) return '650px'
|
||||||
|
return '700px'
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -103,13 +128,13 @@ export default function LoginForm() {
|
|||||||
<div
|
<div
|
||||||
className="bg-white rounded-2xl shadow-2xl flex flex-col items-center relative border-t-4 border-[#8D6B1D]"
|
className="bg-white rounded-2xl shadow-2xl flex flex-col items-center relative border-t-4 border-[#8D6B1D]"
|
||||||
style={{
|
style={{
|
||||||
width: isMobile ? '98vw' : '40vw',
|
width: getFormWidth(),
|
||||||
maxWidth: isMobile ? 'none' : '700px',
|
maxWidth: getFormMaxWidth(),
|
||||||
minWidth: isMobile ? '0' : '400px',
|
minWidth: isMobile ? '0' : '400px',
|
||||||
minHeight: isMobile ? '320px' : '320px',
|
minHeight: isMobile ? '320px' : '320px',
|
||||||
padding: isMobile ? '0.5rem' : '2rem',
|
padding: isMobile ? '0.5rem' : '2rem',
|
||||||
marginTop: isMobile ? '0.5rem' : undefined,
|
marginTop: isMobile ? '0.5rem' : undefined,
|
||||||
transform: isMobile ? undefined : 'scale(0.85)',
|
transform: getFormScale(),
|
||||||
transformOrigin: 'top center',
|
transformOrigin: 'top center',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
@ -199,14 +224,14 @@ export default function LoginForm() {
|
|||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div style={{
|
<div style={{
|
||||||
marginTop: isMobile ? '0.5rem' : '1.5rem',
|
marginTop: isMobile ? '0.5rem' : isTablet ? '1rem' : '1.5rem',
|
||||||
marginBottom: isMobile ? '1.5rem' : '2rem',
|
marginBottom: isMobile ? '1.5rem' : isTablet ? '1.75rem' : '2rem',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}>
|
}}>
|
||||||
<h1
|
<h1
|
||||||
className="mb-2 text-center text-4xl font-extrabold text-[#0F172A] tracking-tight drop-shadow-lg"
|
className="mb-2 text-center text-4xl font-extrabold text-[#0F172A] tracking-tight drop-shadow-lg"
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '2rem' : undefined,
|
fontSize: isMobile ? '2rem' : isTablet ? '2.25rem' : undefined,
|
||||||
marginTop: isMobile ? '0.5rem' : undefined,
|
marginTop: isMobile ? '0.5rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -215,8 +240,8 @@ export default function LoginForm() {
|
|||||||
<p
|
<p
|
||||||
className="mb-8 text-center text-lg text-[#8D6B1D] font-medium"
|
className="mb-8 text-center text-lg text-[#8D6B1D] font-medium"
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '0.95rem' : undefined,
|
fontSize: isMobile ? '0.95rem' : isTablet ? '1rem' : undefined,
|
||||||
marginBottom: isMobile ? '1rem' : undefined,
|
marginBottom: isMobile ? '1rem' : isTablet ? '1.5rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Welcome back! Login to continue.
|
Welcome back! Login to continue.
|
||||||
@ -225,7 +250,7 @@ export default function LoginForm() {
|
|||||||
<form
|
<form
|
||||||
className="space-y-7 w-full"
|
className="space-y-7 w-full"
|
||||||
style={{
|
style={{
|
||||||
gap: isMobile ? '0.75rem' : undefined,
|
gap: isMobile ? '0.75rem' : isTablet ? '1rem' : undefined,
|
||||||
}}
|
}}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
@ -235,7 +260,7 @@ export default function LoginForm() {
|
|||||||
htmlFor="email"
|
htmlFor="email"
|
||||||
className="block text-base font-semibold text-[#0F172A] mb-1"
|
className="block text-base font-semibold text-[#0F172A] mb-1"
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '0.875rem' : undefined,
|
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
|
||||||
marginBottom: isMobile ? '0.25rem' : undefined,
|
marginBottom: isMobile ? '0.25rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -250,8 +275,8 @@ export default function LoginForm() {
|
|||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
className="appearance-none block w-full px-4 py-3 border border-gray-300 rounded-lg placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:border-[#8D6B1D] text-base bg-white text-[#0F172A] transition"
|
className="appearance-none block w-full px-4 py-3 border border-gray-300 rounded-lg placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:border-[#8D6B1D] text-base bg-white text-[#0F172A] transition"
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '0.875rem' : undefined,
|
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
|
||||||
padding: isMobile ? '0.4rem 0.75rem' : undefined,
|
padding: isMobile ? '0.4rem 0.75rem' : isTablet ? '0.5rem 0.875rem' : undefined,
|
||||||
}}
|
}}
|
||||||
placeholder="deine@email.com"
|
placeholder="deine@email.com"
|
||||||
required
|
required
|
||||||
@ -264,7 +289,7 @@ export default function LoginForm() {
|
|||||||
htmlFor="password"
|
htmlFor="password"
|
||||||
className="block text-base font-semibold text-[#0F172A] mb-1"
|
className="block text-base font-semibold text-[#0F172A] mb-1"
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '0.875rem' : undefined,
|
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
|
||||||
marginBottom: isMobile ? '0.25rem' : undefined,
|
marginBottom: isMobile ? '0.25rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -280,8 +305,8 @@ export default function LoginForm() {
|
|||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
className="appearance-none block w-full px-4 py-3 pr-12 border border-gray-300 rounded-lg placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:border-[#8D6B1D] text-base bg-white text-[#0F172A] transition"
|
className="appearance-none block w-full px-4 py-3 pr-12 border border-gray-300 rounded-lg placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:border-[#8D6B1D] text-base bg-white text-[#0F172A] transition"
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '0.875rem' : undefined,
|
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
|
||||||
padding: isMobile ? '0.4rem 2.5rem 0.4rem 0.75rem' : '0.75rem 3rem 0.75rem 1rem',
|
padding: isMobile ? '0.4rem 2.5rem 0.4rem 0.75rem' : isTablet ? '0.5rem 2.75rem 0.5rem 0.875rem' : '0.75rem 3rem 0.75rem 1rem',
|
||||||
}}
|
}}
|
||||||
placeholder="Dein Passwort"
|
placeholder="Dein Passwort"
|
||||||
required
|
required
|
||||||
@ -347,8 +372,8 @@ export default function LoginForm() {
|
|||||||
: 'bg-gradient-to-r from-[#8D6B1D] via-[#A67C20] to-[#C49225] hover:from-[#7A5E1A] hover:to-[#B8851F] focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:ring-offset-2'
|
: 'bg-gradient-to-r from-[#8D6B1D] via-[#A67C20] to-[#C49225] hover:from-[#7A5E1A] hover:to-[#B8851F] focus:outline-none focus:ring-2 focus:ring-[#8D6B1D] focus:ring-offset-2'
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '0.9rem' : undefined,
|
fontSize: isMobile ? '0.9rem' : isTablet ? '0.95rem' : undefined,
|
||||||
padding: isMobile ? '0.6rem 1rem' : undefined,
|
padding: isMobile ? '0.6rem 1rem' : isTablet ? '0.7rem 1.25rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
@ -378,7 +403,7 @@ export default function LoginForm() {
|
|||||||
<div
|
<div
|
||||||
className="mt-10 w-full"
|
className="mt-10 w-full"
|
||||||
style={{
|
style={{
|
||||||
marginTop: isMobile ? '1rem' : undefined,
|
marginTop: isMobile ? '1rem' : isTablet ? '1.5rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@ -388,7 +413,7 @@ export default function LoginForm() {
|
|||||||
<div
|
<div
|
||||||
className="relative flex justify-center text-base"
|
className="relative flex justify-center text-base"
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '0.875rem' : undefined,
|
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<a href="/register" className="px-3 bg-white text-[#8D6B1D]">Noch kein Account?</a>
|
<a href="/register" className="px-3 bg-white text-[#8D6B1D]">Noch kein Account?</a>
|
||||||
@ -397,13 +422,13 @@ export default function LoginForm() {
|
|||||||
<div
|
<div
|
||||||
className="mt-7 text-center"
|
className="mt-7 text-center"
|
||||||
style={{
|
style={{
|
||||||
marginTop: isMobile ? '0.75rem' : undefined,
|
marginTop: isMobile ? '0.75rem' : isTablet ? '1rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<p
|
<p
|
||||||
className="text-base text-slate-700"
|
className="text-base text-slate-700"
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '0.8rem' : undefined,
|
fontSize: isMobile ? '0.8rem' : isTablet ? '0.85rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Profit Planet is available by invitation only.
|
Profit Planet is available by invitation only.
|
||||||
@ -411,7 +436,7 @@ export default function LoginForm() {
|
|||||||
<p
|
<p
|
||||||
className="text-base text-[#8D6B1D] mt-2"
|
className="text-base text-[#8D6B1D] mt-2"
|
||||||
style={{
|
style={{
|
||||||
fontSize: isMobile ? '0.8rem' : undefined,
|
fontSize: isMobile ? '0.8rem' : isTablet ? '0.85rem' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Contact us for an invitation!
|
Contact us for an invitation!
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user