'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}>
}