39948-vm/frontend/src/components/PWALoadingOverlay.tsx
2026-03-19 07:12:29 +04:00

123 lines
3.2 KiB
TypeScript

/**
* PWA Loading Overlay Component
*
* Displays a "We prepare a demo" overlay during first load
* with pulsating animation and progress tracking for asset preloading.
* Uses project theme colors from theme_config_json when available.
*/
import React, { useEffect, useState } from 'react';
type PWALoadingOverlayProps = {
isVisible: boolean;
progress: number; // 0-100
projectName?: string;
themeConfig?: {
primaryColor?: string;
backgroundColor?: string;
textColor?: string;
};
onComplete?: () => void;
};
const PWALoadingOverlay: React.FC<PWALoadingOverlayProps> = ({
isVisible,
progress,
projectName,
themeConfig,
onComplete,
}) => {
const [shouldRender, setShouldRender] = useState(isVisible);
// Handle fade-out animation before unmounting
useEffect(() => {
if (!isVisible && shouldRender) {
const timer = setTimeout(() => {
setShouldRender(false);
onComplete?.();
}, 500); // Match CSS transition duration
return () => clearTimeout(timer);
}
if (isVisible) {
setShouldRender(true);
}
}, [isVisible, shouldRender, onComplete]);
if (!shouldRender) return null;
const primaryColor = themeConfig?.primaryColor || '#3b82f6';
const backgroundColor = themeConfig?.backgroundColor || '#1f2937';
const textColor = themeConfig?.textColor || '#ffffff';
return (
<div
className={`fixed inset-0 z-[9999] flex flex-col items-center justify-center transition-opacity duration-500 ${
isVisible ? 'opacity-100' : 'opacity-0 pointer-events-none'
}`}
style={{ backgroundColor }}
>
{/* Pulsating logo/spinner */}
<div
className='mb-8 h-20 w-20 rounded-full animate-pulse'
style={{
backgroundColor: primaryColor,
boxShadow: `0 0 60px ${primaryColor}40`,
}}
/>
{/* Project name */}
{projectName && (
<h1
className='mb-4 text-2xl font-bold tracking-wide'
style={{ color: textColor }}
>
{projectName}
</h1>
)}
{/* Loading message */}
<p
className='mb-6 text-lg animate-pulse'
style={{ color: textColor, opacity: 0.9 }}
>
We prepare a demo
</p>
{/* Progress bar */}
<div
className='w-64 h-2 rounded-full overflow-hidden'
style={{ backgroundColor: `${textColor}20` }}
>
<div
className='h-full rounded-full transition-all duration-300 ease-out'
style={{
width: `${Math.min(100, Math.max(0, progress))}%`,
backgroundColor: primaryColor,
}}
/>
</div>
{/* Progress percentage */}
<p className='mt-3 text-sm' style={{ color: textColor, opacity: 0.7 }}>
{Math.round(progress)}%
</p>
{/* Loading indicator dots */}
<div className='mt-8 flex gap-2'>
{[0, 1, 2].map((index) => (
<div
key={index}
className='h-2 w-2 rounded-full animate-bounce'
style={{
backgroundColor: primaryColor,
animationDelay: `${index * 0.15}s`,
}}
/>
))}
</div>
</div>
);
};
export default PWALoadingOverlay;