388 lines
9.8 KiB
TypeScript
388 lines
9.8 KiB
TypeScript
"use client";
|
|
import { useState, useEffect } from "react";
|
|
import { usePathname } from "next/navigation";
|
|
import Image from "next/image";
|
|
|
|
interface PreloaderProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
const Preloader = ({ children }: PreloaderProps) => {
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [progress, setProgress] = useState(0);
|
|
const [currentPath, setCurrentPath] = useState("");
|
|
const pathname = usePathname();
|
|
|
|
// Debug mode - set to true to see console logs
|
|
const DEBUG = false;
|
|
|
|
// Skip preloader for faster development/testing (set to true to disable preloader)
|
|
const SKIP_PRELOADER = false;
|
|
|
|
// Fast mode - set to true for even faster loading (200ms total)
|
|
const FAST_MODE = true;
|
|
|
|
useEffect(() => {
|
|
// Only show preloader if path has changed or it's initial load
|
|
if (currentPath !== pathname) {
|
|
if (DEBUG) console.log('Preloader: Starting transition from', currentPath, 'to', pathname);
|
|
setIsLoading(true);
|
|
setProgress(0);
|
|
|
|
// Simulate loading progress - faster and more responsive
|
|
const progressInterval = setInterval(() => {
|
|
setProgress((prev) => {
|
|
if (prev >= 85) {
|
|
clearInterval(progressInterval);
|
|
return 85;
|
|
}
|
|
return prev + Math.random() * 25 + 10; // Faster progress increments
|
|
});
|
|
}, 50); // Reduced interval from 100ms to 50ms for smoother animation
|
|
|
|
// Complete loading much faster - adjust timing based on FAST_MODE
|
|
const loadingDuration = FAST_MODE ? 200 : 400;
|
|
const fadeOutDuration = FAST_MODE ? 50 : 100;
|
|
|
|
const completeTimer = setTimeout(() => {
|
|
setProgress(100);
|
|
setTimeout(() => {
|
|
if (DEBUG) console.log('Preloader: Transition complete');
|
|
setIsLoading(false);
|
|
setCurrentPath(pathname);
|
|
}, fadeOutDuration);
|
|
}, loadingDuration);
|
|
|
|
// Fallback: Force complete after reasonable time
|
|
const fallbackDuration = FAST_MODE ? 800 : 1500;
|
|
const fallbackTimer = setTimeout(() => {
|
|
if (DEBUG) console.log('Preloader: Fallback triggered - force completing');
|
|
setIsLoading(false);
|
|
setCurrentPath(pathname);
|
|
setProgress(100);
|
|
}, fallbackDuration);
|
|
|
|
return () => {
|
|
clearInterval(progressInterval);
|
|
clearTimeout(completeTimer);
|
|
clearTimeout(fallbackTimer);
|
|
};
|
|
}
|
|
}, [pathname, currentPath, DEBUG, FAST_MODE]);
|
|
|
|
// Skip preloader entirely if SKIP_PRELOADER is true
|
|
if (SKIP_PRELOADER) {
|
|
return <>{children}</>;
|
|
}
|
|
|
|
// Don't show preloader if not loading
|
|
if (!isLoading) {
|
|
return <>{children}</>;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="preloader-overlay">
|
|
<div className="preloader-container">
|
|
{/* Logo with shine effect */}
|
|
<div className="preloader-logo">
|
|
<div className="logo-wrapper">
|
|
<Image
|
|
src="/images/logo.png"
|
|
alt="GNX Logo"
|
|
width={80}
|
|
height={60}
|
|
className="logo-image"
|
|
priority
|
|
/>
|
|
<div className="shine-effect"></div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Progress bar */}
|
|
<div className="progress-container">
|
|
<div className="progress-bar">
|
|
<div
|
|
className="progress-fill"
|
|
style={{ width: `${progress}%` }}
|
|
></div>
|
|
</div>
|
|
<div className="progress-text">{Math.round(progress)}%</div>
|
|
</div>
|
|
|
|
{/* Loading text */}
|
|
<div className="loading-text">
|
|
<span className="loading-dots">
|
|
<span>.</span>
|
|
<span>.</span>
|
|
<span>.</span>
|
|
</span>
|
|
</div>
|
|
|
|
{/* Enterprise tagline */}
|
|
<div className="enterprise-tagline">
|
|
<span>Enterprise Solutions</span>
|
|
<span>Excellence in Every Project</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style jsx>{`
|
|
.preloader-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0f0f0f 100%);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 9999;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.preloader-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 2rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.preloader-logo {
|
|
position: relative;
|
|
animation: logoFloat 2s ease-in-out infinite;
|
|
}
|
|
|
|
.logo-wrapper {
|
|
position: relative;
|
|
display: inline-block;
|
|
padding: 1.5rem;
|
|
border-radius: 50%;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
overflow: hidden;
|
|
transition: all 0.2s ease; /* Faster transitions */
|
|
}
|
|
|
|
.logo-image {
|
|
position: relative;
|
|
z-index: 2;
|
|
filter: brightness(1.1);
|
|
transition: all 0.2s ease; /* Faster transitions */
|
|
}
|
|
|
|
.shine-effect {
|
|
position: absolute;
|
|
top: 0;
|
|
left: -100%;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(
|
|
90deg,
|
|
transparent,
|
|
rgba(255, 255, 255, 0.4),
|
|
transparent
|
|
);
|
|
animation: shine 2s infinite;
|
|
z-index: 3;
|
|
}
|
|
|
|
.progress-container {
|
|
width: 200px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.progress-bar {
|
|
width: 100%;
|
|
height: 3px;
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 2px;
|
|
overflow: hidden;
|
|
position: relative;
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #4f46e5, #7c3aed, #ec4899);
|
|
border-radius: 2px;
|
|
transition: width 0.2s ease; /* Faster progress bar animation */
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.progress-fill::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(
|
|
90deg,
|
|
transparent,
|
|
rgba(255, 255, 255, 0.6),
|
|
transparent
|
|
);
|
|
animation: progressShine 1.5s infinite;
|
|
}
|
|
|
|
.progress-text {
|
|
font-size: 0.875rem;
|
|
color: rgba(255, 255, 255, 0.7);
|
|
font-weight: 500;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.loading-text {
|
|
color: rgba(255, 255, 255, 0.6);
|
|
font-size: 0.875rem;
|
|
font-weight: 400;
|
|
letter-spacing: 0.1em;
|
|
}
|
|
|
|
.loading-dots {
|
|
display: inline-flex;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.loading-dots span {
|
|
animation: dotPulse 1.4s infinite ease-in-out;
|
|
animation-fill-mode: both;
|
|
}
|
|
|
|
.loading-dots span:nth-child(1) {
|
|
animation-delay: -0.32s;
|
|
}
|
|
|
|
.loading-dots span:nth-child(2) {
|
|
animation-delay: -0.16s;
|
|
}
|
|
|
|
.loading-dots span:nth-child(3) {
|
|
animation-delay: 0s;
|
|
}
|
|
|
|
.enterprise-tagline {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
margin-top: 1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.enterprise-tagline span {
|
|
font-size: 0.75rem;
|
|
color: rgba(255, 255, 255, 0.5);
|
|
font-weight: 300;
|
|
letter-spacing: 0.15em;
|
|
text-transform: uppercase;
|
|
animation: fadeInUp 0.8s ease-out 0.5s both;
|
|
}
|
|
|
|
.enterprise-tagline span:first-child {
|
|
animation-delay: 0.7s;
|
|
}
|
|
|
|
.enterprise-tagline span:last-child {
|
|
animation-delay: 0.9s;
|
|
}
|
|
|
|
@keyframes logoFloat {
|
|
0%, 100% {
|
|
transform: translateY(0px);
|
|
}
|
|
50% {
|
|
transform: translateY(-10px);
|
|
}
|
|
}
|
|
|
|
@keyframes shine {
|
|
0% {
|
|
left: -100%;
|
|
}
|
|
100% {
|
|
left: 100%;
|
|
}
|
|
}
|
|
|
|
@keyframes progressShine {
|
|
0% {
|
|
transform: translateX(-100%);
|
|
}
|
|
100% {
|
|
transform: translateX(100%);
|
|
}
|
|
}
|
|
|
|
@keyframes dotPulse {
|
|
0%, 80%, 100% {
|
|
opacity: 0.3;
|
|
transform: scale(0.8);
|
|
}
|
|
40% {
|
|
opacity: 1;
|
|
transform: scale(1);
|
|
}
|
|
}
|
|
|
|
@keyframes fadeInUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* Responsive adjustments */
|
|
@media (max-width: 768px) {
|
|
.preloader-container {
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.logo-wrapper {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.logo-image {
|
|
width: 60px;
|
|
height: 45px;
|
|
}
|
|
|
|
.progress-container {
|
|
width: 150px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.preloader-container {
|
|
gap: 1rem;
|
|
}
|
|
|
|
.logo-wrapper {
|
|
padding: 0.75rem;
|
|
}
|
|
|
|
.logo-image {
|
|
width: 50px;
|
|
height: 37px;
|
|
}
|
|
|
|
.progress-container {
|
|
width: 120px;
|
|
}
|
|
}
|
|
`}</style>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Preloader;
|