Files
blackcanyontickets/reactrebuild0825/src/components/loading/LoadingSpinner.tsx
dzinesco 28bfff42d8 feat(error): implement comprehensive error handling and loading states
- Add error boundary components with graceful fallbacks
- Implement loading states with skeleton components
- Create route-level suspense wrapper
- Add error page with recovery options
- Include error boundary demo for testing

Error handling provides resilient user experience with clear feedback
and recovery options when components fail.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-16 12:41:05 -06:00

169 lines
3.8 KiB
TypeScript

import React from 'react';
import { clsx } from 'clsx';
export interface LoadingSpinnerProps {
size?: 'sm' | 'md' | 'lg' | 'xl';
variant?: 'primary' | 'secondary' | 'accent' | 'muted';
overlay?: boolean;
text?: string;
className?: string;
}
/**
* Reusable loading spinner with glassmorphism styling
* Provides smooth animations and multiple size/variant options
*/
export function LoadingSpinner({
size = 'md',
variant = 'primary',
overlay = false,
text,
className
}: LoadingSpinnerProps) {
const sizeClasses = {
sm: 'w-4 h-4',
md: 'w-6 h-6',
lg: 'w-8 h-8',
xl: 'w-12 h-12'
};
const variantClasses = {
primary: 'text-primary-500',
secondary: 'text-secondary-500',
accent: 'text-gold-500',
muted: 'text-text-muted'
};
const textSizeClasses = {
sm: 'text-sm',
md: 'text-base',
lg: 'text-lg',
xl: 'text-xl'
};
const spinnerElement = (
<div className="flex flex-col items-center justify-center gap-md">
{/* Spinner SVG */}
<svg
className={clsx(
'animate-spin',
sizeClasses[size],
variantClasses[variant],
className
)}
fill="none"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
{/* Loading Text */}
{text && (
<p className={clsx(
'text-text-secondary animate-pulse',
textSizeClasses[size]
)}>
{text}
</p>
)}
</div>
);
// Render as overlay if specified
if (overlay) {
return (
<div className="fixed inset-0 bg-bg-overlay backdrop-blur-sm z-50 flex items-center justify-center">
<div className="bg-glass-bg border border-glass-border rounded-lg p-6xl shadow-glass-lg">
{spinnerElement}
</div>
</div>
);
}
return spinnerElement;
}
/**
* Pulse animation component for skeleton loading states
*/
export function PulseLoader({ className }: { className?: string }) {
return (
<div className={clsx('animate-pulse bg-glass-bg rounded', className)} />
);
}
/**
* Shimmer effect component for advanced skeleton loading
*/
export function ShimmerLoader({
className,
children
}: {
className?: string;
children?: React.ReactNode;
}) {
return (
<div className={clsx('relative overflow-hidden', className)}>
{children}
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/10 to-transparent animate-shimmer" />
</div>
);
}
/**
* Dots loading animation
*/
export function DotsLoader({
size = 'md',
variant = 'primary',
className
}: Pick<LoadingSpinnerProps, 'size' | 'variant' | 'className'>) {
const dotSizeClasses = {
sm: 'w-1 h-1',
md: 'w-2 h-2',
lg: 'w-3 h-3',
xl: 'w-4 h-4'
};
const variantClasses = {
primary: 'bg-primary-500',
secondary: 'bg-secondary-500',
accent: 'bg-gold-500',
muted: 'bg-text-muted'
};
return (
<div className={clsx('flex items-center space-x-1', className)}>
{[0, 1, 2].map((index) => (
<div
key={index}
className={clsx(
'rounded-full animate-bounce',
dotSizeClasses[size],
variantClasses[variant]
)}
style={{
animationDelay: `${index * 0.1}s`,
animationDuration: '0.6s'
}}
/>
))}
</div>
);
}
export default LoadingSpinner;