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

{message}

); } 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, }; }