beautify: page tranistion / loginform
This commit is contained in:
parent
5709f48dc3
commit
f7205ed8f6
@ -2,34 +2,102 @@
|
||||
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
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 pathname = usePathname();
|
||||
const DELAY_MS = 200;
|
||||
const EXIT_DURATION = 0.7; // slow the fade/slide-out a bit
|
||||
|
||||
const variants = {
|
||||
hidden: { opacity: 0, x: 0, y: 20 },
|
||||
enter: { opacity: 1, x: 0, y: 0 },
|
||||
exit: { opacity: 0, x: 0, y: -20 },
|
||||
};
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [showOverlay, setShowOverlay] = useState(true);
|
||||
const [overlayExit, setOverlayExit] = useState(false);
|
||||
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 (
|
||||
<AnimatePresence
|
||||
mode="wait"
|
||||
onExitComplete={() => window.scrollTo(0, 0)}
|
||||
>
|
||||
<motion.div
|
||||
key={pathname}
|
||||
variants={variants}
|
||||
initial="hidden"
|
||||
animate="enter"
|
||||
exit="exit"
|
||||
transition={{ type: 'tween', duration: 0.3 }}
|
||||
className="w-full"
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
<>
|
||||
<AnimatePresence mode="wait" onExitComplete={() => window.scrollTo(0, 0)}>
|
||||
<motion.div
|
||||
key={pathname}
|
||||
variants={{
|
||||
hidden: { opacity: 0, x: 0, y: 20 },
|
||||
enter: { opacity: 1, x: 0, y: 0 },
|
||||
exit: { opacity: 0, x: 0, y: -20 },
|
||||
}}
|
||||
initial="hidden"
|
||||
animate="enter"
|
||||
exit="exit"
|
||||
transition={{ type: 'tween', duration: 0.3 }}
|
||||
className="w-full"
|
||||
>
|
||||
{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 (
|
||||
<div
|
||||
@ -103,13 +128,13 @@ export default function LoginForm() {
|
||||
<div
|
||||
className="bg-white rounded-2xl shadow-2xl flex flex-col items-center relative border-t-4 border-[#8D6B1D]"
|
||||
style={{
|
||||
width: isMobile ? '98vw' : '40vw',
|
||||
maxWidth: isMobile ? 'none' : '700px',
|
||||
width: getFormWidth(),
|
||||
maxWidth: getFormMaxWidth(),
|
||||
minWidth: isMobile ? '0' : '400px',
|
||||
minHeight: isMobile ? '320px' : '320px',
|
||||
padding: isMobile ? '0.5rem' : '2rem',
|
||||
marginTop: isMobile ? '0.5rem' : undefined,
|
||||
transform: isMobile ? undefined : 'scale(0.85)',
|
||||
transform: getFormScale(),
|
||||
transformOrigin: 'top center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@ -199,14 +224,14 @@ export default function LoginForm() {
|
||||
|
||||
{/* Content */}
|
||||
<div style={{
|
||||
marginTop: isMobile ? '0.5rem' : '1.5rem',
|
||||
marginBottom: isMobile ? '1.5rem' : '2rem',
|
||||
marginTop: isMobile ? '0.5rem' : isTablet ? '1rem' : '1.5rem',
|
||||
marginBottom: isMobile ? '1.5rem' : isTablet ? '1.75rem' : '2rem',
|
||||
width: '100%',
|
||||
}}>
|
||||
<h1
|
||||
className="mb-2 text-center text-4xl font-extrabold text-[#0F172A] tracking-tight drop-shadow-lg"
|
||||
style={{
|
||||
fontSize: isMobile ? '2rem' : undefined,
|
||||
fontSize: isMobile ? '2rem' : isTablet ? '2.25rem' : undefined,
|
||||
marginTop: isMobile ? '0.5rem' : undefined,
|
||||
}}
|
||||
>
|
||||
@ -215,8 +240,8 @@ export default function LoginForm() {
|
||||
<p
|
||||
className="mb-8 text-center text-lg text-[#8D6B1D] font-medium"
|
||||
style={{
|
||||
fontSize: isMobile ? '0.95rem' : undefined,
|
||||
marginBottom: isMobile ? '1rem' : undefined,
|
||||
fontSize: isMobile ? '0.95rem' : isTablet ? '1rem' : undefined,
|
||||
marginBottom: isMobile ? '1rem' : isTablet ? '1.5rem' : undefined,
|
||||
}}
|
||||
>
|
||||
Welcome back! Login to continue.
|
||||
@ -225,7 +250,7 @@ export default function LoginForm() {
|
||||
<form
|
||||
className="space-y-7 w-full"
|
||||
style={{
|
||||
gap: isMobile ? '0.75rem' : undefined,
|
||||
gap: isMobile ? '0.75rem' : isTablet ? '1rem' : undefined,
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
@ -235,7 +260,7 @@ export default function LoginForm() {
|
||||
htmlFor="email"
|
||||
className="block text-base font-semibold text-[#0F172A] mb-1"
|
||||
style={{
|
||||
fontSize: isMobile ? '0.875rem' : undefined,
|
||||
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
|
||||
marginBottom: isMobile ? '0.25rem' : undefined,
|
||||
}}
|
||||
>
|
||||
@ -250,8 +275,8 @@ export default function LoginForm() {
|
||||
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"
|
||||
style={{
|
||||
fontSize: isMobile ? '0.875rem' : undefined,
|
||||
padding: isMobile ? '0.4rem 0.75rem' : undefined,
|
||||
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
|
||||
padding: isMobile ? '0.4rem 0.75rem' : isTablet ? '0.5rem 0.875rem' : undefined,
|
||||
}}
|
||||
placeholder="deine@email.com"
|
||||
required
|
||||
@ -264,7 +289,7 @@ export default function LoginForm() {
|
||||
htmlFor="password"
|
||||
className="block text-base font-semibold text-[#0F172A] mb-1"
|
||||
style={{
|
||||
fontSize: isMobile ? '0.875rem' : undefined,
|
||||
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
|
||||
marginBottom: isMobile ? '0.25rem' : undefined,
|
||||
}}
|
||||
>
|
||||
@ -280,8 +305,8 @@ export default function LoginForm() {
|
||||
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"
|
||||
style={{
|
||||
fontSize: isMobile ? '0.875rem' : undefined,
|
||||
padding: isMobile ? '0.4rem 2.5rem 0.4rem 0.75rem' : '0.75rem 3rem 0.75rem 1rem',
|
||||
fontSize: isMobile ? '0.875rem' : isTablet ? '0.9rem' : undefined,
|
||||
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"
|
||||
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'
|
||||
}`}
|
||||
style={{
|
||||
fontSize: isMobile ? '0.9rem' : undefined,
|
||||
padding: isMobile ? '0.6rem 1rem' : undefined,
|
||||
fontSize: isMobile ? '0.9rem' : isTablet ? '0.95rem' : undefined,
|
||||
padding: isMobile ? '0.6rem 1rem' : isTablet ? '0.7rem 1.25rem' : undefined,
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
@ -378,7 +403,7 @@ export default function LoginForm() {
|
||||
<div
|
||||
className="mt-10 w-full"
|
||||
style={{
|
||||
marginTop: isMobile ? '1rem' : undefined,
|
||||
marginTop: isMobile ? '1rem' : isTablet ? '1.5rem' : undefined,
|
||||
}}
|
||||
>
|
||||
<div className="relative">
|
||||
@ -388,7 +413,7 @@ export default function LoginForm() {
|
||||
<div
|
||||
className="relative flex justify-center text-base"
|
||||
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>
|
||||
@ -397,13 +422,13 @@ export default function LoginForm() {
|
||||
<div
|
||||
className="mt-7 text-center"
|
||||
style={{
|
||||
marginTop: isMobile ? '0.75rem' : undefined,
|
||||
marginTop: isMobile ? '0.75rem' : isTablet ? '1rem' : undefined,
|
||||
}}
|
||||
>
|
||||
<p
|
||||
className="text-base text-slate-700"
|
||||
style={{
|
||||
fontSize: isMobile ? '0.8rem' : undefined,
|
||||
fontSize: isMobile ? '0.8rem' : isTablet ? '0.85rem' : undefined,
|
||||
}}
|
||||
>
|
||||
Profit Planet is available by invitation only.
|
||||
@ -411,7 +436,7 @@ export default function LoginForm() {
|
||||
<p
|
||||
className="text-base text-[#8D6B1D] mt-2"
|
||||
style={{
|
||||
fontSize: isMobile ? '0.8rem' : undefined,
|
||||
fontSize: isMobile ? '0.8rem' : isTablet ? '0.85rem' : undefined,
|
||||
}}
|
||||
>
|
||||
Contact us for an invitation!
|
||||
|
||||
Loading…
Reference in New Issue
Block a user