import React from 'react'; import { cn } from '@/lib/utils'; /** * DataListProps - Propriétés du composant DataList * * @interface DataListProps * @template T - Type des éléments de la liste */ interface DataListProps { /** * Tableau d'éléments à afficher */ items: T[]; /** * Fonction pour rendre chaque élément de la liste * * @param {T} item - Élément à rendre * @param {number} index - Index de l'élément dans la liste * @returns {React.ReactNode} Élément React à afficher * * @example * ```tsx *
{user.name}
} * keyExtractor={(user) => user.id} * /> * ``` */ renderItem: (item: T, index: number) => React.ReactNode; /** * Fonction pour extraire une clé unique de chaque élément * * @param {T} item - Élément * @returns {string} Clé unique */ keyExtractor: (item: T) => string; /** * Message à afficher lorsque la liste est vide * * @default 'No items found' */ emptyMessage?: string; /** * Si `true`, affiche un état de chargement avec des skeletons * * @default false */ loading?: boolean; /** * Message d'erreur à afficher si une erreur survient */ error?: string; /** * Classes CSS personnalisées pour le conteneur */ className?: string; /** * Classes CSS personnalisées pour chaque élément */ itemClassName?: string; } /** * DataList - Composant de liste générique avec gestion des états * * Composant réutilisable pour afficher des listes d'éléments avec support pour : * - État de chargement (skeletons) * - État d'erreur * - État vide * - Rendu personnalisé des éléments * * @example * ```tsx * // Liste simple * ( *
*

{user.name}

*

{user.email}

*
* )} * keyExtractor={(user) => user.id} * /> * ``` * * @example * ```tsx * // Avec états de chargement et d'erreur * } * keyExtractor={(track) => track.id} * loading={isLoading} * error={error} * emptyMessage="Aucune piste trouvée" * /> * ``` * * @component * @template T - Type des éléments de la liste * @param {DataListProps} props - Propriétés du composant * @returns {JSX.Element} Liste d'éléments avec gestion des états */ export function DataList({ items, renderItem, keyExtractor, emptyMessage = 'No items found', loading = false, error, className, itemClassName, }: DataListProps) { if (loading) { return (
{Array.from({ length: 3 }).map((_, index) => (
))}
); } if (error) { return (

{error}

); } if (items.length === 0) { return (

{emptyMessage}

); } return (
{items.map((item, index) => (
{renderItem(item, index)}
))}
); } /** * ModalProps - Propriétés du composant Modal * * @interface ModalProps */ interface ModalProps { /** * Si `true`, le modal est ouvert */ isOpen: boolean; /** * Fonction appelée pour fermer le modal */ onClose: () => void; /** * Titre du modal (affiché dans le header) */ title?: string; /** * Contenu du modal */ children: React.ReactNode; /** * Taille du modal * * - `sm`: max-w-md * - `md`: max-w-lg - par défaut * - `lg`: max-w-2xl * - `xl`: max-w-4xl * * @default 'md' */ size?: 'sm' | 'md' | 'lg' | 'xl'; /** * Si `true`, affiche le bouton de fermeture dans le header * * @default true */ showCloseButton?: boolean; } /** * Modal - Composant de modal avec backdrop et fermeture * * Composant modal simple avec backdrop cliquable et bouton de fermeture. * * @example * ```tsx * setIsOpen(false)} * title="Titre du modal" * > *

Contenu du modal

*
* ``` * * @component */ export const Modal: React.FC = ({ isOpen, onClose, title, children, size = 'md', showCloseButton = true, }) => { if (!isOpen) return null; const sizeClasses = { sm: 'max-w-md', md: 'max-w-lg', lg: 'max-w-2xl', xl: 'max-w-4xl', }; return (
{/* Backdrop */}
{/* Modal */}
{/* Header */} {(title || showCloseButton) && (
{title && (

{title}

)} {showCloseButton && ( )}
)} {/* Content */}
{children}
); }; /** * DropdownProps - Propriétés du composant Dropdown * * @interface DropdownProps */ interface DropdownProps { /** * Élément déclencheur du dropdown (bouton, etc.) */ trigger: React.ReactNode; /** * Contenu du dropdown (menu items, etc.) */ children: React.ReactNode; /** * Si `true`, le dropdown est ouvert */ isOpen: boolean; /** * Fonction appelée pour basculer l'état ouvert/fermé */ onToggle: () => void; /** * Alignement du dropdown par rapport au trigger * * - `left`: Aligné à gauche * - `right`: Aligné à droite * * @default 'left' */ align?: 'left' | 'right'; /** * Classes CSS personnalisées pour le dropdown */ className?: string; } /** * Dropdown - Composant de menu déroulant basique * * Composant de dropdown avec backdrop et positionnement relatif au trigger. * * @example * ```tsx * Menu} * isOpen={isOpen} * onToggle={() => setIsOpen(!isOpen)} * align="right" * > *
* * *
*
* ``` * * @component */ export const Dropdown: React.FC = ({ trigger, children, isOpen, onToggle, align = 'left', className, }) => { return (
{trigger}
{isOpen && ( <> {/* Backdrop */}
{/* Dropdown */}
{children}
)}
); };