import React from 'react'; import { cn } from '@/lib/utils'; import { LoadingSpinner } from './loading-spinner'; import { Loader2 } from 'lucide-react'; /** * CRITIQUE FIX #20: Composant LoadingState standardisé pour uniformiser les états de chargement * * Ce composant fournit une interface cohérente pour afficher les états de chargement * à travers toute l'application, améliorant l'UX et la cohérence visuelle. */ export interface LoadingStateProps { /** * Si true, affiche l'état de chargement */ isLoading?: boolean; /** * Type de chargement à afficher * - 'spinner': Spinner centré avec texte optionnel (par défaut) * - 'inline': Spinner inline avec texte à côté * - 'skeleton': Skeleton loader pour le contenu * - 'minimal': Spinner minimal sans conteneur */ variant?: 'spinner' | 'inline' | 'skeleton' | 'minimal'; /** * Taille du spinner */ size?: 'sm' | 'md' | 'lg'; /** * Texte à afficher pendant le chargement */ text?: string; /** * Message à afficher si aucun texte n'est fourni */ defaultText?: string; /** * Classes CSS personnalisées */ className?: string; /** * Contenu à afficher quand isLoading est false */ children?: React.ReactNode; /** * Si true, affiche un skeleton loader au lieu d'un spinner * (utilisé avec variant='skeleton') */ showSkeleton?: boolean; } /** * LoadingState - Composant standardisé pour les états de chargement * * CRITIQUE FIX #20: Standardise tous les états de chargement dans l'application * * @example * ```tsx * // Spinner centré (par défaut) * * * // Spinner inline * * * // Skeleton loader * *
Contenu à charger
*
* * // Avec contenu conditionnel * *
Contenu chargé
*
* ``` */ export function LoadingState({ isLoading = false, variant = 'spinner', size = 'md', text, defaultText = 'Chargement...', className, children, showSkeleton = false, }: LoadingStateProps) { // Si pas de chargement, afficher le contenu if (!isLoading && !showSkeleton) { return <>{children}; } const displayText = text || defaultText; // Variant: spinner (par défaut) - Spinner centré avec texte if (variant === 'spinner') { return (
); } // Variant: inline - Spinner avec texte à côté if (variant === 'inline') { const sizeClasses = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6', }; return (
); } // Variant: skeleton - Skeleton loader pour le contenu if (variant === 'skeleton' || showSkeleton) { return (
{children || ( <>
)}
); } // Variant: minimal - Spinner minimal sans conteneur const sizeClasses = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6', }; return ( ); } /** * LoadingStateWrapper - Wrapper pour afficher un état de chargement autour du contenu * * CRITIQUE FIX #20: Wrapper pratique pour les composants qui chargent des données * * @example * ```tsx * * * * ``` */ export interface LoadingStateWrapperProps extends Omit { /** * Contenu à afficher quand isLoading est false */ children: React.ReactNode; /** * Variant à utiliser pour l'état de chargement */ loadingVariant?: LoadingStateProps['variant']; } export function LoadingStateWrapper({ isLoading, children, loadingVariant = 'spinner', text, defaultText, className, size, }: LoadingStateWrapperProps) { if (isLoading) { return ( ); } return <>{children}; }