'use client' import React from 'react' /** * Loading Indicator Component * * Shows progress during async operations. * Supports different display modes: spinner, bar, dots, etc. */ export interface LoadingIndicatorProps { /** * Whether to show the loading indicator * @default true */ show?: boolean /** * Loading message to display */ message?: string /** * Variant: 'spinner', 'bar', 'dots', 'pulse' * @default 'spinner' */ variant?: 'spinner' | 'bar' | 'dots' | 'pulse' /** * Size of the indicator: 'small', 'medium', 'large' * @default 'medium' */ size?: 'small' | 'medium' | 'large' /** * Whether to show full page overlay * @default false */ fullPage?: boolean /** * CSS class name for custom styling */ className?: string /** * Custom style overrides */ style?: React.CSSProperties } export function LoadingIndicator({ show = true, message, variant = 'spinner', size = 'medium', fullPage = false, className, style, }: LoadingIndicatorProps) { if (!show) { return null } const sizeMap = { small: '24px', medium: '40px', large: '60px', } const containerStyle: React.CSSProperties = fullPage ? { position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0, 0, 0, 0.5)', zIndex: 9999, ...style, } : { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '20px', ...style, } return (
{variant === 'spinner' && } {variant === 'bar' && } {variant === 'dots' && } {variant === 'pulse' && } {message && (

{message}

)}
) } /** * Spinner icon component */ interface IconProps { size: string } function SpinnerIcon({ size }: IconProps) { return (
) } function PulseIcon({ size }: IconProps) { return (
) } /** * Progress bar component */ interface ProgressBarProps { size: 'small' | 'medium' | 'large' } function ProgressBar({ size }: ProgressBarProps) { const heightMap = { small: '2px', medium: '4px', large: '6px', } return (
) } /** * Animated dots component */ interface DotsAnimationProps { size: 'small' | 'medium' | 'large' } function DotsAnimation({ size }: DotsAnimationProps) { const dotMap = { small: '6px', medium: '10px', large: '14px', } const dotSize = dotMap[size] return (
{[0, 1, 2].map((i) => (
))}
) } /** * Inline loading spinner for buttons and text */ export interface InlineLoaderProps { loading?: boolean size?: 'small' | 'medium' style?: React.CSSProperties } export function InlineLoader({ loading = true, size = 'small', style }: InlineLoaderProps) { if (!loading) { return null } const sizeMap = { small: '16px', medium: '20px', } return (
) } /** * Loading state for async operations with skeleton fallback */ export interface AsyncLoadingProps { isLoading: boolean error?: Error | string | null children: React.ReactNode skeletonComponent?: React.ReactNode errorComponent?: React.ReactNode loadingMessage?: string } export function AsyncLoading({ isLoading, error, children, skeletonComponent, errorComponent, loadingMessage, }: AsyncLoadingProps) { if (isLoading) { return skeletonComponent ?? } if (error) { return errorComponent ??
Error loading content
} return <>{children} }