From ac65373c36a13ed4e9aa5118b3a4e3d1e3e5bdc5 Mon Sep 17 00:00:00 2001 From: senke Date: Fri, 16 Jan 2026 00:45:52 +0100 Subject: [PATCH] ui: create Spinner component for inline loading states (Action 8.3.1.4) - Created Spinner.tsx component for inline use in buttons and UI elements - Size variants: sm, md, lg - Color variants: default (kodo-cyan), muted, white, current - Uses Loader2 from lucide-react with Kodo design system styling - Includes accessibility attributes (sr-only label) - Different from LoadingSpinner (which is for full-page states) - Task 8.3.1.4 complete --- EXHAUSTIVE_TODO_LIST.md | 13 ++- apps/web/src/components/ui/Spinner.tsx | 108 +++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/components/ui/Spinner.tsx diff --git a/EXHAUSTIVE_TODO_LIST.md b/EXHAUSTIVE_TODO_LIST.md index 4c3062f5a..f36e54bb2 100644 --- a/EXHAUSTIVE_TODO_LIST.md +++ b/EXHAUSTIVE_TODO_LIST.md @@ -2942,11 +2942,18 @@ Critical path dependencies: - **Result**: All high and medium priority mutation buttons now have loading states. Low priority buttons (auto-triggered notifications) can be enhanced incrementally if needed. - **Rollback**: Remove loading states -- [ ] **Action 8.3.1.4**: Create Spinner component (if doesn't exist) +- [x] **Action 8.3.1.4**: Create Spinner component (if doesn't exist) - **Scope**: `apps/web/src/components/ui/Spinner.tsx` (create) - Reusable spinner component - - **Dependencies**: None + - **Dependencies**: None ✅ - **Risk**: LOW 🔒 - - **Validation**: Spinner component works + - **Validation**: ✅ Spinner component created: + - **File**: Created `apps/web/src/components/ui/Spinner.tsx` + - **Component**: Simple, reusable inline spinner component + - **Features**: Size variants (sm, md, lg), color variants (default, muted, white, current), accessibility (aria-label, role="status") + - **Design**: Uses Kodo design system colors (kodo-cyan by default), wraps Loader2 from lucide-react + - **Purpose**: Optimized for inline use in buttons and UI elements (different from LoadingSpinner which is for full-page states) + - **Pattern**: Follows existing component patterns with TypeScript interfaces and JSDoc documentation + - **Result**: Reusable spinner component ready for use in loading states - **Rollback**: Delete component - [ ] **Action 8.3.1.5**: Use Spinner in loading states diff --git a/apps/web/src/components/ui/Spinner.tsx b/apps/web/src/components/ui/Spinner.tsx new file mode 100644 index 000000000..2dd68c211 --- /dev/null +++ b/apps/web/src/components/ui/Spinner.tsx @@ -0,0 +1,108 @@ +import { Loader2 } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +/** + * SpinnerProps - Propriétés du composant Spinner + * + * @interface SpinnerProps + */ +export interface SpinnerProps { + /** + * Taille du spinner + * + * - `sm`: Petit (h-4 w-4) - pour les boutons et éléments compacts + * - `md`: Moyen (h-5 w-5) - par défaut + * - `lg`: Grand (h-6 w-6) - pour les éléments plus grands + * + * @default 'md' + */ + size?: 'sm' | 'md' | 'lg'; + + /** + * Couleur du spinner + * + * - `default`: Couleur par défaut (kodo-cyan pour le thème Kodo) + * - `muted`: Couleur atténuée (text-muted-foreground) + * - `white`: Blanc + * - `current`: Utilise la couleur du texte parent + * + * @default 'default' + */ + variant?: 'default' | 'muted' | 'white' | 'current'; + + /** + * Classes CSS personnalisées + */ + className?: string; + + /** + * Label d'accessibilité + * + * @default 'Chargement en cours' + */ + 'aria-label'?: string; +} + +/** + * Spinner - Composant de spinner réutilisable pour les états de chargement inline + * + * Composant de spinner optimisé pour l'utilisation inline dans les boutons, + * formulaires et autres éléments UI. Utilise le design system Kodo. + * + * Différences avec LoadingSpinner: + * - LoadingSpinner: Pour les états de chargement de page complète avec conteneur + * - Spinner: Pour les spinners inline dans les boutons et éléments UI + * + * @example + * ```tsx + * // Spinner simple dans un bouton + * + * + * // Spinner avec couleur personnalisée + * + * + * // Spinner avec classes personnalisées + * + * ``` + * + * @component + * @param {SpinnerProps} props - Propriétés du composant + * @returns {JSX.Element} Spinner animé + */ +export function Spinner({ + size = 'md', + variant = 'default', + className, + 'aria-label': ariaLabel = 'Chargement en cours', +}: SpinnerProps) { + const sizeClasses = { + sm: 'h-4 w-4', + md: 'h-5 w-5', + lg: 'h-6 w-6', + }; + + const variantClasses = { + default: 'text-kodo-cyan', + muted: 'text-muted-foreground', + white: 'text-white', + current: 'text-current', + }; + + return ( + <> +