'use client';
import { useState, useEffect, useRef, useCallback } from 'react';
import { cn } from '@/lib/utils';
import {
Skeleton,
SkeletonHero,
SkeletonList,
SkeletonCard,
SkeletonForm,
} from './skeleton';
type LoadingVariant = 'hero' | 'list' | 'card' | 'form' | 'custom' | 'spinner';
interface LoadingStateProps {
isLoading: boolean;
children: React.ReactNode;
variant?: LoadingVariant;
listItems?: number;
formFields?: number;
className?: string;
delayMs?: number;
customSkeleton?: React.ReactNode;
}
export function LoadingState({
isLoading,
children,
variant = 'custom',
listItems = 3,
formFields = 4,
className,
delayMs = 150,
customSkeleton,
}: LoadingStateProps) {
const [showSkeleton, setShowSkeleton] = useState(false);
const prevIsLoading = useRef(isLoading);
useEffect(() => {
if (isLoading !== prevIsLoading.current) {
prevIsLoading.current = isLoading;
if (!isLoading) {
setShowSkeleton(false);
return;
}
const timer = setTimeout(() => setShowSkeleton(true), delayMs);
return () => clearTimeout(timer);
}
return undefined;
}, [isLoading, delayMs]);
if (isLoading && showSkeleton) {
return (
{variant === 'hero' &&
}
{variant === 'list' &&
}
{variant === 'card' && (
{[1, 2, 3].map((i) => (
))}
)}
{variant === 'form' &&
}
{variant === 'custom' && (customSkeleton ||
)}
{variant === 'spinner' && (
)}
);
}
return <>{children}>;
}
interface SpinnerProps {
size?: 'sm' | 'md' | 'lg';
className?: string;
}
export function Spinner({ size = 'md', className }: SpinnerProps) {
const sizeClasses = {
sm: 'w-4 h-4',
md: 'w-8 h-8',
lg: 'w-12 h-12',
};
return (
);
}
interface PageLoaderProps {
isLoading: boolean;
message?: string;
}
export function PageLoader({ isLoading, message = '正在加载...' }: PageLoaderProps) {
if (!isLoading) return null;
return (
);
}
export function useLoadingState(initialState = false, delayMs = 150) {
const [isLoading, setIsLoading] = useState(initialState);
const [shouldShow, setShouldShow] = useState(false);
useEffect(() => {
if (!isLoading) {
setShouldShow(false);
return;
}
const timer = setTimeout(() => setShouldShow(true), delayMs);
return () => clearTimeout(timer);
}, [isLoading, delayMs]);
const startLoading = useCallback(() => setIsLoading(true), []);
const stopLoading = useCallback(() => setIsLoading(false), []);
return {
isLoading,
shouldShow,
startLoading,
stopLoading,
setLoading: setIsLoading,
};
}