import { Loader2, CheckCircle, AlertCircle, X } from 'lucide-react'; import { useState, useEffect } from 'react'; // Animated Loading Spinner export function LoadingSpinner({ size = 'md', className = '' }) { const sizeClasses = { sm: 'h-5 w-5', md: 'h-8 w-8', lg: 'h-12 w-12', xl: 'h-16 w-16' }; return (
); } // Full page loading export function PageLoader() { return (

Загрузка...

); } // Skeleton loader for cards export function CardSkeleton({ count = 1 }) { return ( <> {Array.from({ length: count }).map((_, i) => (
))} ); } // Skeleton loader for list items export function ListSkeleton({ count = 5 }) { return (
{Array.from({ length: count }).map((_, i) => (
))}
); } // Toast notification component export function Toast({ message, type = 'success', onClose, duration = 3000 }) { const [isVisible, setIsVisible] = useState(true); const [isExiting, setIsExiting] = useState(false); useEffect(() => { const timer = setTimeout(() => { handleClose(); }, duration); return () => clearTimeout(timer); }, [duration]); const handleClose = () => { setIsExiting(true); setTimeout(() => { setIsVisible(false); onClose?.(); }, 200); }; if (!isVisible) return null; const typeStyles = { success: 'bg-green-50 border-green-200 text-green-800', error: 'bg-red-50 border-red-200 text-red-800', warning: 'bg-amber-50 border-amber-200 text-amber-800', info: 'bg-blue-50 border-blue-200 text-blue-800' }; const icons = { success: , error: , warning: , info: }; return (
{icons[type]}

{message}

); } // Animated counter component export function AnimatedCounter({ value, duration = 1000 }) { const [displayValue, setDisplayValue] = useState(0); useEffect(() => { if (typeof value !== 'number') { setDisplayValue(value); return; } let startTime; const startValue = displayValue; const endValue = value; const animate = (timestamp) => { if (!startTime) startTime = timestamp; const progress = Math.min((timestamp - startTime) / duration, 1); // Ease out const easeOut = 1 - Math.pow(1 - progress, 3); const currentValue = Math.round(startValue + (endValue - startValue) * easeOut); setDisplayValue(currentValue); if (progress < 1) { requestAnimationFrame(animate); } }; requestAnimationFrame(animate); }, [value, duration]); return {displayValue}; } // Animated presence wrapper export function AnimatedPresence({ children, show, className = '' }) { const [shouldRender, setShouldRender] = useState(show); useEffect(() => { if (show) setShouldRender(true); }, [show]); const handleAnimationEnd = () => { if (!show) setShouldRender(false); }; if (!shouldRender) return null; return (
{children}
); } // Ripple button wrapper export function RippleButton({ children, onClick, className = '', ...props }) { const handleClick = (e) => { const button = e.currentTarget; const ripple = document.createElement('span'); const rect = button.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); const x = e.clientX - rect.left - size / 2; const y = e.clientY - rect.top - size / 2; ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${x}px`; ripple.style.top = `${y}px`; ripple.className = 'absolute bg-white/30 rounded-full animate-ping pointer-events-none'; button.appendChild(ripple); setTimeout(() => ripple.remove(), 500); onClick?.(e); }; return ( ); } // Pulsing dot indicator export function PulsingDot({ color = 'green', size = 'sm' }) { const sizeClasses = { sm: 'w-2 h-2', md: 'w-3 h-3', lg: 'w-4 h-4' }; const colorClasses = { green: 'bg-green-500', red: 'bg-red-500', amber: 'bg-amber-500', blue: 'bg-blue-500' }; return ( ); } // Progress bar export function ProgressBar({ value, max = 100, showLabel = false, className = '' }) { const percentage = Math.min(Math.max((value / max) * 100, 0), 100); return (
{showLabel && (

{Math.round(percentage)}%

)}
); }