91 lines
2.4 KiB
TypeScript
91 lines
2.4 KiB
TypeScript
/**
|
|
* CanvasLoadingSpinner Component
|
|
*
|
|
* Loading spinner overlay for canvas contexts.
|
|
* Shows during video preparation/buffering.
|
|
* Includes delay to avoid flashing for quick operations.
|
|
*/
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import { PRELOAD_CONFIG } from '../config/preload.config';
|
|
|
|
interface CanvasLoadingSpinnerProps {
|
|
/** Whether the spinner is visible */
|
|
isVisible: boolean;
|
|
/** Loading message to display */
|
|
message?: string;
|
|
/** Spinner size */
|
|
size?: 'sm' | 'md' | 'lg';
|
|
/** Loading progress (0-100) */
|
|
progress?: number;
|
|
/** Z-index for stacking (default: 100) */
|
|
zIndex?: number;
|
|
}
|
|
|
|
const CanvasLoadingSpinner: React.FC<CanvasLoadingSpinnerProps> = ({
|
|
isVisible,
|
|
message,
|
|
size = 'md',
|
|
progress,
|
|
zIndex = 100,
|
|
}) => {
|
|
// Delayed visibility - only show after SPINNER_DELAY_MS
|
|
const [showSpinner, setShowSpinner] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (isVisible) {
|
|
// Start timer to show spinner after delay
|
|
const timer = setTimeout(() => {
|
|
setShowSpinner(true);
|
|
}, PRELOAD_CONFIG.ui.spinnerDelayMs);
|
|
|
|
return () => clearTimeout(timer);
|
|
} else {
|
|
// Hide immediately when loading completes
|
|
setShowSpinner(false);
|
|
}
|
|
}, [isVisible]);
|
|
|
|
if (!showSpinner) return null;
|
|
|
|
const sizeClasses = {
|
|
sm: 'w-8 h-8 border-2',
|
|
md: 'w-12 h-12 border-[3px]',
|
|
lg: 'w-16 h-16 border-4',
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className='absolute inset-0 flex flex-col items-center justify-center pointer-events-none'
|
|
style={{ zIndex }}
|
|
>
|
|
{/* Spinner with subtle shadow for visibility on any background */}
|
|
<div
|
|
className='relative'
|
|
style={{ filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.5))' }}
|
|
>
|
|
{/* Spinner ring */}
|
|
<div
|
|
className={`${sizeClasses[size]} rounded-full border-white/30 border-t-white animate-spin`}
|
|
style={{ borderStyle: 'solid' }}
|
|
/>
|
|
{/* Progress indicator (optional) */}
|
|
{progress !== undefined && (
|
|
<div className='absolute inset-0 flex items-center justify-center'>
|
|
<span className='text-white text-xs font-medium drop-shadow-md'>
|
|
{Math.round(progress)}%
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
{message && (
|
|
<p className='mt-3 text-white/90 text-sm font-medium drop-shadow-md'>
|
|
{message}
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default CanvasLoadingSpinner;
|