analytica-frontend-lib 1.3.76 → 1.3.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/Modal/Modal.tsx","../../src/utils/utils.ts","../../src/components/Button/Button.tsx","../../src/components/Modal/utils/videoUtils.ts","../../src/components/Text/Text.tsx","../../src/components/ProgressBar/ProgressBar.tsx","../../src/components/ProgressModal/ProgressModal.tsx"],"sourcesContent":["import { ReactNode, useEffect, useId } from 'react';\nimport { X } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\nimport Button from '../Button/Button';\nimport {\n isYouTubeUrl,\n getYouTubeVideoId,\n getYouTubeEmbedUrl,\n} from './utils/videoUtils';\n\n/**\n * Lookup table for size classes\n */\nconst SIZE_CLASSES = {\n xs: 'max-w-[360px]',\n sm: 'max-w-[420px]',\n md: 'max-w-[510px]',\n lg: 'max-w-[640px]',\n xl: 'max-w-[970px]',\n} as const;\n\n/**\n * Modal component props interface\n */\ntype ModalProps = {\n contentClassName?: string;\n /** Whether the modal is open */\n isOpen: boolean;\n /** Function to close the modal */\n onClose: () => void;\n /**\n * Modal title. Aceita string OU ReactNode pra permitir elementos inline\n * (ex: botão de voltar + label). Como é renderizado dentro de <h2>,\n * quando ReactNode use APENAS phrasing content (`<span>`, `<button>`,\n * ícones) — nunca `<div>` ou `<p>`.\n */\n title: ReactNode;\n /** Modal description/content */\n children?: ReactNode;\n /** Size of the modal */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n /** Additional CSS classes for the modal content */\n className?: string;\n /** Whether pressing Escape should close the modal */\n closeOnEscape?: boolean;\n /** Footer content (typically buttons) */\n footer?: ReactNode;\n /** Hide the close button */\n hideCloseButton?: boolean;\n /** Modal variant */\n variant?: 'default' | 'activity';\n /** Description for activity variant */\n description?: string;\n /** Image URL for activity variant */\n image?: string;\n /** Alt text for activity image (leave empty for decorative images) */\n imageAlt?: string;\n /** Action link for activity variant */\n actionLink?: string;\n /** Action button label for activity variant */\n actionLabel?: string;\n};\n\n/**\n * Modal component for Analytica Ensino platforms\n *\n * A flexible modal component with multiple size variants and customizable behavior.\n *\n * @param isOpen - Whether the modal is currently open\n * @param onClose - Callback function called when the modal should be closed\n * @param title - The title displayed at the top of the modal\n * @param children - The main content of the modal\n * @param size - The size variant (xs, sm, md, lg, xl)\n * @param className - Additional CSS classes for the modal content\n * @param closeOnEscape - Whether pressing Escape closes the modal (default: true)\n * @param footer - Footer content, typically action buttons\n * @param hideCloseButton - Whether to hide the X close button (default: false)\n * @returns A modal overlay with content\n *\n * @example\n * ```tsx\n * <Modal\n * isOpen={isModalOpen}\n * onClose={() => setIsModalOpen(false)}\n * title=\"Invite your team\"\n * size=\"md\"\n * footer={\n * <div className=\"flex gap-3\">\n * <Button variant=\"outline\" onClick={() => setIsModalOpen(false)}>Cancel</Button>\n * <Button variant=\"solid\" onClick={handleExplore}>Explore</Button>\n * </div>\n * }\n * >\n * Elevate user interactions with our versatile modals.\n * </Modal>\n * ```\n */\nconst Modal = ({\n isOpen,\n onClose,\n title,\n children,\n size = 'md',\n className = '',\n closeOnEscape = true,\n footer,\n hideCloseButton = false,\n variant = 'default',\n description,\n image,\n imageAlt,\n actionLink,\n actionLabel,\n contentClassName = '',\n}: ModalProps) => {\n const titleId = useId();\n\n // Handle escape key\n useEffect(() => {\n if (!isOpen || !closeOnEscape) return;\n\n const handleEscape = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [isOpen, closeOnEscape, onClose]);\n\n // Handle body scroll lock and scrollbar shift fix\n useEffect(() => {\n if (!isOpen) return;\n\n // Calculate scrollbar width before hiding overflow\n const scrollbarWidth =\n window.innerWidth - document.documentElement.clientWidth;\n\n // Save original styles\n const originalOverflow = document.body.style.overflow;\n const originalPaddingRight = document.body.style.paddingRight;\n\n // Apply scroll lock\n document.body.style.overflow = 'hidden';\n\n // Fix scrollbar shift: add padding to compensate for lost scrollbar\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${scrollbarWidth}px`;\n\n // Create overlay to cover the padding area with backdrop color\n const overlay = document.createElement('div');\n overlay.id = 'modal-scrollbar-overlay';\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n right: 0;\n width: ${scrollbarWidth}px;\n height: 100vh;\n background-color: rgb(0 0 0 / 0.6);\n z-index: 40;\n pointer-events: none;\n `;\n document.body.appendChild(overlay);\n }\n\n return () => {\n // Restore original styles\n document.body.style.overflow = originalOverflow;\n document.body.style.paddingRight = originalPaddingRight;\n\n // Remove overlay\n const overlay = document.getElementById('modal-scrollbar-overlay');\n if (overlay) {\n overlay.remove();\n }\n };\n }, [isOpen]);\n\n if (!isOpen) return null;\n\n const sizeClasses = SIZE_CLASSES[size];\n const baseClasses =\n 'bg-secondary-50 rounded-3xl shadow-hard-shadow-2 border border-border-100 w-full mx-4';\n // Reset dialog default styles to prevent positioning issues\n const dialogResetClasses = 'p-0 m-0 border-none outline-none static';\n const modalClasses = cn(\n baseClasses,\n sizeClasses,\n dialogResetClasses,\n className\n );\n\n // Normalize URLs missing protocol\n const normalizeUrl = (href: string) =>\n /^https?:\\/\\//i.test(href) ? href : `https://${href}`;\n\n // Handle action link click\n const handleActionClick = () => {\n if (actionLink) {\n window.open(normalizeUrl(actionLink), '_blank', 'noopener,noreferrer');\n }\n };\n\n // Activity variant rendering\n if (variant === 'activity') {\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs border-none p-0 m-0 w-full cursor-default\">\n <dialog\n className={modalClasses}\n aria-labelledby={titleId}\n aria-modal=\"true\"\n open\n >\n {/* Header simples com X */}\n <div className=\"flex justify-end p-6 pb-0\">\n {!hideCloseButton && (\n <button\n onClick={onClose}\n className=\"p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2\"\n aria-label=\"Fechar modal\"\n >\n <X size={18} />\n </button>\n )}\n </div>\n\n {/* Conteúdo centralizado */}\n <div className=\"flex flex-col items-center px-6 pb-6 gap-5\">\n {/* Imagem ilustrativa */}\n {image && (\n <div className=\"flex justify-center\">\n <img\n src={image}\n alt={imageAlt ?? ''}\n className=\"w-[122px] h-[122px] object-contain\"\n />\n </div>\n )}\n\n {/* Título */}\n <h2\n id={titleId}\n className=\"text-lg font-semibold text-text-950 text-center\"\n >\n {title}\n </h2>\n\n {/* Descrição */}\n {description && (\n <p className=\"text-sm font-normal text-text-400 text-center max-w-md leading-[21px]\">\n {description}\n </p>\n )}\n\n {/* Ação: Botão ou Vídeo Embedado */}\n {actionLink && (\n <div className=\"w-full\">\n {(() => {\n const normalized = normalizeUrl(actionLink);\n const isYT = isYouTubeUrl(normalized);\n if (!isYT) return null;\n const id = getYouTubeVideoId(normalized);\n if (!id) {\n return (\n <Button\n variant=\"solid\"\n action=\"primary\"\n size=\"large\"\n className=\"w-full\"\n onClick={handleActionClick}\n >\n {actionLabel || 'Iniciar Atividade'}\n </Button>\n );\n }\n return (\n <iframe\n src={getYouTubeEmbedUrl(id)}\n className=\"w-full aspect-video rounded-lg\"\n allowFullScreen\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n title=\"Vídeo YouTube\"\n />\n );\n })()}\n {!isYouTubeUrl(normalizeUrl(actionLink)) && (\n <Button\n variant=\"solid\"\n action=\"primary\"\n size=\"large\"\n className=\"w-full\"\n onClick={handleActionClick}\n >\n {actionLabel || 'Iniciar Atividade'}\n </Button>\n )}\n </div>\n )}\n </div>\n </dialog>\n </div>\n );\n }\n\n // Default variant rendering\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs border-none p-0 m-0 w-full cursor-default\">\n <dialog\n className={modalClasses}\n aria-labelledby={titleId}\n aria-modal=\"true\"\n open\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-6\">\n <h2 id={titleId} className=\"text-lg font-semibold text-text-950\">\n {title}\n </h2>\n {!hideCloseButton && (\n <button\n onClick={onClose}\n className=\"p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2\"\n aria-label=\"Fechar modal\"\n >\n <X size={18} />\n </button>\n )}\n </div>\n\n {/* Content */}\n {children && (\n <div className={cn('px-6 pb-6', contentClassName)}>\n <div\n className={cn(\n 'text-text-500 font-normal text-sm leading-6',\n contentClassName?.includes('flex') &&\n 'flex flex-col flex-1 min-h-0'\n )}\n >\n {children}\n </div>\n </div>\n )}\n\n {/* Footer */}\n {footer && (\n <div className=\"flex justify-end gap-3 px-6 pb-6\">{footer}</div>\n )}\n </dialog>\n </div>\n );\n};\n\nexport default Modal;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Lookup table for variant and action class combinations\n */\nconst VARIANT_ACTION_CLASSES = {\n solid: {\n primary:\n 'bg-primary-950 text-text border border-primary-950 hover:bg-primary-800 hover:border-primary-800 focus-visible:outline-none focus-visible:bg-primary-950 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-primary-700 active:border-primary-700 disabled:bg-primary-500 disabled:border-primary-500 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-text-950 text-text border border-text-800 hover:bg-text-800 hover:border-text-950 focus-visible:outline-none focus-visible:bg-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-text-700 active:border-text-700 disabled:bg-text-500 disabled:border-text-500 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-success-500 text-text border border-success-500 hover:bg-success-600 hover:border-success-600 focus-visible:outline-none focus-visible:bg-success-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-success-700 active:border-success-700 disabled:bg-success-500 disabled:border-success-500 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-error-500 text-text border border-error-500 hover:bg-error-600 hover:border-error-600 focus-visible:outline-none focus-visible:bg-error-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-error-700 active:border-error-700 disabled:bg-error-500 disabled:border-error-500 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n outline: {\n primary:\n 'bg-transparent text-primary-950 border border-primary-950 hover:bg-background-50 hover:text-primary-400 hover:border-primary-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 active:border-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-transparent text-text-950 border border-text-800 hover:bg-background-50 hover:text-text-700 hover:border-text-700 focus-visible:border-0 focus-visible:outline-none focus-visible:text-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-text-700 active:border-text-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 border border-success-300 hover:bg-background-50 hover:text-success-400 hover:border-success-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 active:border-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 border border-error-300 hover:bg-background-50 hover:text-error-400 hover:border-error-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 active:border-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n link: {\n primary:\n 'bg-transparent text-primary-950 hover:text-primary-400 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-transparent text-text-950 hover:text-text-800 focus-visible:outline-none focus-visible:text-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-text-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 hover:text-success-400 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 hover:text-error-400 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n} as const;\n\n/**\n * Lookup table for size classes\n */\nconst SIZE_CLASSES = {\n 'extra-small': 'text-xs px-3.5 py-2',\n small: 'text-sm px-4 py-2.5',\n medium: 'text-md px-5 py-2.5',\n large: 'text-lg px-6 py-3',\n 'extra-large': 'text-lg px-7 py-3.5',\n} as const;\n\n/**\n * Button component props interface\n */\ntype ButtonProps = {\n /** Content to be displayed inside the button */\n children: ReactNode;\n /** Ícone à esquerda do texto */\n iconLeft?: ReactNode;\n /** Ícone à direita do texto */\n iconRight?: ReactNode;\n /** Size of the button */\n size?: 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large';\n /** Visual variant of the button. Use 'raw' for no default styling */\n variant?: 'solid' | 'outline' | 'link' | 'raw';\n /** Action type of the button */\n action?: 'primary' | 'secondary' | 'positive' | 'negative';\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * Button component for Analytica Ensino platforms\n *\n * A flexible button component with multiple variants, sizes and actions.\n *\n * @param children - The content to display inside the button\n * @param size - The size variant (extra-small, small, medium, large, extra-large)\n * @param variant - The visual style variant (solid, outline, link)\n * @param action - The action type (primary, secondary, positive, negative)\n * @param className - Additional CSS classes\n * @param props - All other standard button HTML attributes\n * @returns A styled button element\n *\n * @example\n * ```tsx\n * <Button variant=\"solid\" action=\"primary\" size=\"medium\" onClick={() => console.log('clicked')}>\n * Click me\n * </Button>\n * ```\n */\nconst Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n children,\n iconLeft,\n iconRight,\n size = 'medium',\n variant = 'solid',\n action = 'primary',\n className = '',\n disabled,\n type = 'button',\n ...props\n },\n ref\n ) => {\n // Raw variant: no default styling, only className\n if (variant === 'raw') {\n return (\n <button\n ref={ref}\n className={className}\n disabled={disabled}\n type={type}\n {...props}\n >\n {iconLeft && (\n <span className=\"mr-2 flex items-center\">{iconLeft}</span>\n )}\n {children}\n {iconRight && (\n <span className=\"ml-2 flex items-center\">{iconRight}</span>\n )}\n </button>\n );\n }\n\n // Get classes from lookup tables\n const sizeClasses = SIZE_CLASSES[size];\n const variantClasses = VARIANT_ACTION_CLASSES[variant][action];\n\n const baseClasses =\n 'inline-flex items-center justify-center rounded-full cursor-pointer font-medium';\n\n return (\n <button\n ref={ref}\n className={cn(baseClasses, variantClasses, sizeClasses, className)}\n disabled={disabled}\n type={type}\n {...props}\n >\n {iconLeft && <span className=\"mr-2 flex items-center\">{iconLeft}</span>}\n {children}\n {iconRight && (\n <span className=\"ml-2 flex items-center\">{iconRight}</span>\n )}\n </button>\n );\n }\n);\n\nButton.displayName = 'Button';\n\nexport default Button;\n","/**\n * Video utilities for Modal component\n *\n * Utilities to handle YouTube video embedding and URL detection\n */\n\n/**\n * Check if a given URL is a YouTube URL\n *\n * @param url - The URL to check\n * @returns true if the URL is from YouTube, false otherwise\n */\nexport const isYouTubeUrl = (url: string): boolean => {\n const youtubeRegex =\n /^(https?:\\/\\/)?((www|m|music)\\.)?(youtube\\.com|youtu\\.be|youtube-nocookie\\.com)\\/.+/i;\n return youtubeRegex.test(url);\n};\n\n/**\n * Validate if hostname is a legitimate YouTube host\n *\n * @param host - The hostname to validate\n * @returns The type of YouTube host or null if invalid\n */\nconst isValidYouTubeHost = (\n host: string\n): 'youtu.be' | 'youtube' | 'nocookie' | null => {\n if (host === 'youtu.be') return 'youtu.be';\n\n const isValidYouTubeCom =\n host === 'youtube.com' ||\n (host.endsWith('.youtube.com') &&\n /^(www|m|music)\\.youtube\\.com$/.test(host));\n\n if (isValidYouTubeCom) return 'youtube';\n\n const isValidNoCookie =\n host === 'youtube-nocookie.com' ||\n (host.endsWith('.youtube-nocookie.com') &&\n /^(www|m|music)\\.youtube-nocookie\\.com$/.test(host));\n\n if (isValidNoCookie) return 'nocookie';\n\n return null;\n};\n\n/**\n * Extract video ID from youtu.be path\n *\n * @param pathname - The URL pathname\n * @returns The video ID or null\n */\nconst extractYoutuBeId = (pathname: string): string | null => {\n const firstSeg = pathname.split('/').filter(Boolean)[0];\n return firstSeg || null;\n};\n\n/**\n * Extract video ID from YouTube or nocookie domains\n *\n * @param pathname - The URL pathname\n * @param searchParams - The URL search parameters\n * @returns The video ID or null\n */\nconst extractYouTubeId = (\n pathname: string,\n searchParams: URLSearchParams\n): string | null => {\n const parts = pathname.split('/').filter(Boolean);\n const [first, second] = parts;\n\n if (first === 'embed' && second) return second;\n if (first === 'shorts' && second) return second;\n if (first === 'live' && second) return second;\n\n const v = searchParams.get('v');\n if (v) return v;\n\n return null;\n};\n\n/**\n * Extract YouTube video ID from URL\n *\n * @param url - The YouTube URL\n * @returns The video ID if found, null otherwise\n */\nexport const getYouTubeVideoId = (url: string): string | null => {\n try {\n const u = new URL(url);\n const hostType = isValidYouTubeHost(u.hostname.toLowerCase());\n\n if (!hostType) return null;\n\n if (hostType === 'youtu.be') {\n return extractYoutuBeId(u.pathname);\n }\n\n return extractYouTubeId(u.pathname, u.searchParams);\n } catch {\n return null;\n }\n};\n\n/**\n * Generate YouTube embed URL for iframe\n *\n * @param videoId - The YouTube video ID\n * @returns The embed URL for the video\n */\nexport const getYouTubeEmbedUrl = (videoId: string): string => {\n return `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=0&rel=0&modestbranding=1`;\n};\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses: string;\n let weightClasses: string;\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n","import { ReactNode } from 'react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Progress bar size variants\n */\ntype ProgressBarSize = 'small' | 'medium';\n\n/**\n * Progress bar color variants\n */\ntype ProgressBarVariant = 'blue' | 'green';\n\n/**\n * Progress bar layout variants\n */\ntype ProgressBarLayout = 'default' | 'stacked' | 'compact';\n\n/**\n * Size configurations using Tailwind classes\n */\nconst SIZE_CLASSES = {\n small: {\n container: 'h-1', // 4px height (h-1 = 4px in Tailwind)\n bar: 'h-1', // 4px height for the fill bar\n spacing: 'gap-2', // 8px gap between label and progress bar\n layout: 'flex-col', // vertical layout for small\n borderRadius: 'rounded-full', // 9999px border radius\n },\n medium: {\n container: 'h-2', // 8px height (h-2 = 8px in Tailwind)\n bar: 'h-2', // 8px height for the fill bar\n spacing: 'gap-2', // 8px gap between progress bar and label\n layout: 'flex-row items-center', // horizontal layout for medium\n borderRadius: 'rounded-lg', // 8px border radius\n },\n} as const;\n\n/**\n * Color configurations using design system colors\n */\nconst VARIANT_CLASSES = {\n blue: {\n background: 'bg-background-300', // Background track color (#D5D4D4)\n fill: 'bg-primary-700', // Blue for activity progress (#2271C4)\n },\n green: {\n background: 'bg-background-300', // Background track color (#D5D4D4)\n fill: 'bg-success-200', // Green for performance (#84D3A2)\n },\n} as const;\n\n/**\n * Type for size classes\n */\ntype SizeClassType = (typeof SIZE_CLASSES)[keyof typeof SIZE_CLASSES];\n\n/**\n * Type for variant classes\n */\ntype VariantClassType = (typeof VARIANT_CLASSES)[keyof typeof VARIANT_CLASSES];\n\n/**\n * Common props shared across all layout components\n */\ninterface BaseLayoutProps {\n className: string;\n label: ReactNode;\n showPercentage: boolean;\n showHitCount: boolean;\n labelClassName: string;\n percentageClassName: string;\n clampedValue: number;\n max: number;\n percentage: number;\n variantClasses: VariantClassType;\n}\n\n/**\n * Dimensions configuration for layouts\n */\ninterface LayoutDimensions {\n width: string;\n height: string;\n}\n\n/**\n * Props for StackedLayout component\n */\ninterface StackedLayoutProps extends BaseLayoutProps {\n dimensions: LayoutDimensions;\n}\n\n/**\n * Props for CompactLayout component\n */\ninterface CompactLayoutProps extends BaseLayoutProps {\n dimensions: LayoutDimensions;\n}\n\n/**\n * Props for DefaultLayout component\n */\ninterface DefaultLayoutProps {\n className: string;\n size: ProgressBarSize;\n sizeClasses: SizeClassType;\n variantClasses: VariantClassType;\n label: ReactNode;\n showPercentage: boolean;\n labelClassName: string;\n percentageClassName: string;\n clampedValue: number;\n max: number;\n percentage: number;\n}\n\n/**\n * ProgressBar component props interface\n */\nexport type ProgressBarProps = {\n /** Progress value between 0 and 100 */\n value: number;\n /** Maximum value (defaults to 100) */\n max?: number;\n /** Size variant of the progress bar */\n size?: ProgressBarSize;\n /** Color variant of the progress bar */\n variant?: ProgressBarVariant;\n /** Layout variant of the progress bar */\n layout?: ProgressBarLayout;\n /** Optional label to display */\n label?: ReactNode;\n /** Show percentage text */\n showPercentage?: boolean;\n /**\n * Show hit count (e.g., \"28 de 30\") instead of percentage\n *\n * PRIORITY: When both showHitCount and showPercentage are true,\n * showHitCount takes precedence (stacked and compact layouts only).\n * Default layout does not support showHitCount.\n */\n showHitCount?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Label CSS classes */\n labelClassName?: string;\n /** Percentage text CSS classes */\n percentageClassName?: string;\n /** Custom width for stacked layout (defaults to w-[380px]) */\n stackedWidth?: string;\n /** Custom height for stacked layout (defaults to h-[35px]) */\n stackedHeight?: string;\n /** Custom width for compact layout (defaults to w-[131px]) */\n compactWidth?: string;\n /** Custom height for compact layout (defaults to h-[24px]) */\n compactHeight?: string;\n};\n\n/**\n * Helper function to calculate safe progress values\n */\nconst calculateProgressValues = (value: number, max: number) => {\n const safeValue = isNaN(value) ? 0 : value;\n const clampedValue = Math.max(0, Math.min(safeValue, max));\n const percentage = max === 0 ? 0 : (clampedValue / max) * 100;\n\n return { clampedValue, percentage };\n};\n\n/**\n * Helper function to determine if header content should be shown\n */\nconst shouldShowHeader = (\n label: ReactNode,\n showPercentage: boolean,\n showHitCount: boolean\n): boolean => {\n return !!(label || showPercentage || showHitCount);\n};\n\n/**\n * Centralized function to determine display priority and content\n *\n * PRIORITY ORDER (consistent across all layouts):\n * 1. showHitCount (highest priority) - displays \"X de Y\" format\n * 2. showPercentage - displays \"X%\" format\n * 3. label (lowest priority) - displays custom label\n *\n * @param showHitCount - Whether to show hit count format\n * @param showPercentage - Whether to show percentage format\n * @param label - Custom label to display\n * @param clampedValue - Current progress value\n * @param max - Maximum progress value\n * @param percentage - Calculated percentage value\n * @returns Object with content type and formatted content\n */\nconst getDisplayPriority = (\n showHitCount: boolean,\n showPercentage: boolean,\n label: ReactNode,\n clampedValue: number,\n max: number,\n percentage: number\n) => {\n if (showHitCount) {\n return {\n type: 'hitCount' as const,\n content: `${Math.round(clampedValue)} de ${max}`,\n hasMetrics: true,\n };\n }\n\n if (showPercentage) {\n return {\n type: 'percentage' as const,\n content: `${Math.round(percentage)}%`,\n hasMetrics: true,\n };\n }\n\n return {\n type: 'label' as const,\n content: label,\n hasMetrics: false,\n };\n};\n\n/**\n * Parameters for compact layout configuration\n */\ninterface CompactLayoutConfigParams {\n showPercentage: boolean;\n showHitCount: boolean;\n percentage: number;\n clampedValue: number;\n max: number;\n label: ReactNode;\n percentageClassName: string;\n labelClassName: string;\n}\n\n/**\n * Helper function to get compact layout configuration\n *\n * PRIORITY ORDER (consistent across all layouts):\n * 1. showHitCount (highest priority) - displays \"X de Y\" format\n * 2. showPercentage - displays \"X%\" format\n * 3. label (lowest priority) - displays custom label\n *\n * When both showHitCount and showPercentage are true, showHitCount takes precedence.\n */\nconst getCompactLayoutConfig = ({\n showPercentage,\n showHitCount,\n percentage,\n clampedValue,\n max,\n label,\n percentageClassName,\n labelClassName,\n}: CompactLayoutConfigParams) => {\n // Use centralized priority logic for consistency\n const displayPriority = getDisplayPriority(\n showHitCount,\n showPercentage,\n label,\n clampedValue,\n max,\n percentage\n );\n\n return {\n color: displayPriority.hasMetrics ? 'text-primary-600' : 'text-primary-700',\n className: displayPriority.hasMetrics\n ? percentageClassName\n : labelClassName,\n content: displayPriority.content,\n };\n};\n\n/**\n * Helper function to get default layout display configuration\n *\n * PRIORITY ORDER for default layout (showHitCount is not supported):\n * 1. showPercentage (when enabled, takes precedence over label)\n * 2. label (shown only when showPercentage is false)\n *\n * Note: Default layout does not support showHitCount feature.\n */\nconst getDefaultLayoutDisplayConfig = (\n size: ProgressBarSize,\n label: ReactNode,\n showPercentage: boolean\n) => ({\n showHeader: size === 'small' && !!(label || showPercentage),\n showPercentage: size === 'medium' && showPercentage,\n showLabel: size === 'medium' && !!label && !showPercentage, // Only show label when percentage is not shown\n});\n\n/**\n * Helper function to render hit count or percentage display for stacked layout\n *\n * PRIORITY ORDER (consistent across all layouts):\n * 1. showHitCount (highest priority) - displays \"X de Y\" format\n * 2. showPercentage - displays \"X%\" format\n *\n * When both showHitCount and showPercentage are true, showHitCount takes precedence.\n */\nconst renderStackedHitCountDisplay = (\n showHitCount: boolean,\n showPercentage: boolean,\n clampedValue: number,\n max: number,\n percentage: number,\n percentageClassName: string\n): ReactNode => {\n if (!showHitCount && !showPercentage) return null;\n\n // Use centralized priority logic for consistency\n const displayPriority = getDisplayPriority(\n showHitCount,\n showPercentage,\n null, // label is not relevant for stacked layout metrics display\n clampedValue,\n max,\n percentage\n );\n\n return (\n <div\n className={cn(\n 'text-xs font-medium leading-[14px] text-right',\n percentageClassName\n )}\n >\n {displayPriority.type === 'hitCount' ? (\n <>\n <span className=\"text-success-200\">{Math.round(clampedValue)}</span>\n <span className=\"text-text-600\"> de {max}</span>\n </>\n ) : (\n <Text size=\"xs\" weight=\"medium\" className=\"text-success-200\">\n {Math.round(percentage)}%\n </Text>\n )}\n </div>\n );\n};\n\n/**\n * Base progress bar component with common rendering logic\n */\nconst ProgressBarBase = ({\n clampedValue,\n max,\n percentage,\n label,\n variantClasses,\n containerClassName,\n fillClassName,\n}: {\n clampedValue: number;\n max: number;\n percentage: number;\n label: ReactNode;\n variantClasses: VariantClassType;\n containerClassName: string;\n fillClassName: string;\n}) => (\n <div\n className={cn(\n containerClassName,\n variantClasses.background,\n 'overflow-hidden relative'\n )}\n >\n <progress\n value={clampedValue}\n max={max}\n aria-label={\n typeof label === 'string'\n ? `${label}: ${Math.round(percentage)}% complete`\n : `Progress: ${Math.round(percentage)}% of ${max}`\n }\n className=\"absolute inset-0 w-full h-full opacity-0\"\n />\n <div\n className={cn(\n fillClassName,\n variantClasses.fill,\n 'transition-all duration-300 ease-out'\n )}\n style={{ width: `${percentage}%` }}\n />\n </div>\n);\n\n/**\n * Stacked layout component\n */\nconst StackedLayout = ({\n className,\n label,\n showPercentage,\n showHitCount,\n labelClassName,\n percentageClassName,\n clampedValue,\n max,\n percentage,\n variantClasses,\n dimensions,\n}: StackedLayoutProps) => (\n <div\n className={cn(\n 'flex flex-col items-start gap-2',\n dimensions.width,\n dimensions.height,\n className\n )}\n >\n {shouldShowHeader(label, showPercentage, showHitCount) && (\n <div className=\"flex flex-row justify-between items-center w-full h-[19px]\">\n {label && (\n <Text\n as=\"div\"\n size=\"md\"\n weight=\"medium\"\n className={cn('text-text-600 leading-[19px]', labelClassName)}\n >\n {label}\n </Text>\n )}\n\n {renderStackedHitCountDisplay(\n showHitCount,\n showPercentage,\n clampedValue,\n max,\n percentage,\n percentageClassName\n )}\n </div>\n )}\n\n <ProgressBarBase\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n label={label}\n variantClasses={variantClasses}\n containerClassName=\"w-full h-2 rounded-lg\"\n fillClassName=\"h-2 rounded-lg shadow-hard-shadow-3\"\n />\n </div>\n);\n\n/**\n * Compact layout component\n */\nconst CompactLayout = ({\n className,\n label,\n showPercentage,\n showHitCount,\n labelClassName,\n percentageClassName,\n clampedValue,\n max,\n percentage,\n variantClasses,\n dimensions,\n}: CompactLayoutProps) => {\n const {\n color,\n className: compactClassName,\n content,\n } = getCompactLayoutConfig({\n showPercentage,\n showHitCount,\n percentage,\n clampedValue,\n max,\n label,\n percentageClassName,\n labelClassName,\n });\n\n return (\n <div\n className={cn(\n 'flex flex-col items-start gap-1',\n dimensions.width,\n dimensions.height,\n className\n )}\n >\n {shouldShowHeader(label, showPercentage, showHitCount) && (\n <Text\n as=\"div\"\n size=\"sm\"\n weight=\"medium\"\n color={color}\n className={cn('leading-4 w-full', compactClassName)}\n >\n {content}\n </Text>\n )}\n\n <ProgressBarBase\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n label={label}\n variantClasses={variantClasses}\n containerClassName=\"w-full h-1 rounded-full\"\n fillClassName=\"h-1 rounded-full\"\n />\n </div>\n );\n};\n\n/**\n * Default layout component\n */\nconst DefaultLayout = ({\n className,\n size,\n sizeClasses,\n variantClasses,\n label,\n showPercentage,\n labelClassName,\n percentageClassName,\n clampedValue,\n max,\n percentage,\n}: DefaultLayoutProps) => {\n const gapClass = size === 'medium' ? 'gap-2' : sizeClasses.spacing;\n const progressBarClass = size === 'medium' ? 'flex-grow' : 'w-full';\n const displayConfig = getDefaultLayoutDisplayConfig(\n size,\n label,\n showPercentage\n );\n\n return (\n <div className={cn('flex', sizeClasses.layout, gapClass, className)}>\n {displayConfig.showHeader && (\n <div className=\"flex flex-row items-center justify-between w-full\">\n {label && (\n <Text\n as=\"div\"\n size=\"xs\"\n weight=\"medium\"\n className={cn(\n 'text-text-950 leading-none tracking-normal text-center',\n labelClassName\n )}\n >\n {label}\n </Text>\n )}\n\n {showPercentage && (\n <Text\n size=\"xs\"\n weight=\"medium\"\n className={cn(\n 'text-text-950 leading-none tracking-normal text-center',\n percentageClassName\n )}\n >\n {Math.round(percentage)}%\n </Text>\n )}\n </div>\n )}\n\n <ProgressBarBase\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n label={label}\n variantClasses={variantClasses}\n containerClassName={cn(\n progressBarClass,\n sizeClasses.container,\n sizeClasses.borderRadius\n )}\n fillClassName={cn(\n sizeClasses.bar,\n sizeClasses.borderRadius,\n 'shadow-hard-shadow-3'\n )}\n />\n\n {displayConfig.showPercentage && (\n <Text\n size=\"xs\"\n weight=\"medium\"\n className={cn(\n 'text-text-950 leading-none tracking-normal text-center flex-none',\n percentageClassName\n )}\n >\n {Math.round(percentage)}%\n </Text>\n )}\n\n {displayConfig.showLabel && (\n <Text\n as=\"div\"\n size=\"xs\"\n weight=\"medium\"\n className={cn(\n 'text-text-950 leading-none tracking-normal text-center flex-none',\n labelClassName\n )}\n >\n {label}\n </Text>\n )}\n </div>\n );\n};\n\n/**\n * ProgressBar component for Analytica Ensino platforms\n *\n * A progress bar component with size and color variants designed for tracking\n * activity progress (blue) and performance metrics (green).\n * Uses the Analytica Ensino Design System colors from styles.css with automatic\n * light/dark mode support. Includes Text component integration for consistent typography.\n *\n * CONTENT DISPLAY PRIORITY (Consistent across all layouts):\n * 1. showHitCount (highest) - \"X de Y\" format (stacked/compact only)\n * 2. showPercentage - \"X%\" format\n * 3. label (lowest) - Custom label text\n *\n * When multiple display options are enabled, higher priority options take precedence.\n *\n * @example\n * ```tsx\n * // Basic progress bar\n * <ProgressBar value={65} />\n *\n * // Activity progress (blue)\n * <ProgressBar variant=\"blue\" value={45} label=\"Progress\" showPercentage />\n *\n * // Performance metrics (green)\n * <ProgressBar variant=\"green\" size=\"medium\" value={85} label=\"Performance\" />\n *\n * // Small size with custom max value\n * <ProgressBar size=\"small\" value={3} max={5} showPercentage />\n *\n * // Stacked layout with fixed width and hit count\n * <ProgressBar layout=\"stacked\" variant=\"green\" value={28} max={30} label=\"Fáceis\" showHitCount />\n *\n * // Compact layout for small cards\n * <ProgressBar layout=\"compact\" variant=\"blue\" value={70} label=\"Questão 08\" />\n * ```\n */\nconst ProgressBar = ({\n value,\n max = 100,\n size = 'medium',\n variant = 'blue',\n layout = 'default',\n label,\n showPercentage = false,\n showHitCount = false,\n className = '',\n labelClassName = '',\n percentageClassName = '',\n stackedWidth,\n stackedHeight,\n compactWidth,\n compactHeight,\n}: ProgressBarProps) => {\n const { clampedValue, percentage } = calculateProgressValues(value, max);\n const sizeClasses = SIZE_CLASSES[size];\n const variantClasses = VARIANT_CLASSES[variant];\n\n if (layout === 'stacked') {\n return (\n <StackedLayout\n className={className}\n label={label}\n showPercentage={showPercentage}\n showHitCount={showHitCount}\n labelClassName={labelClassName}\n percentageClassName={percentageClassName}\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n variantClasses={variantClasses}\n dimensions={{\n width: stackedWidth ?? 'w-[380px]',\n height: stackedHeight ?? 'h-[35px]',\n }}\n />\n );\n }\n\n if (layout === 'compact') {\n return (\n <CompactLayout\n className={className}\n label={label}\n showPercentage={showPercentage}\n showHitCount={showHitCount}\n labelClassName={labelClassName}\n percentageClassName={percentageClassName}\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n variantClasses={variantClasses}\n dimensions={{\n width: compactWidth ?? 'w-[131px]',\n height: compactHeight ?? 'h-[24px]',\n }}\n />\n );\n }\n\n return (\n <DefaultLayout\n className={className}\n size={size}\n sizeClasses={sizeClasses}\n variantClasses={variantClasses}\n label={label}\n showPercentage={showPercentage}\n labelClassName={labelClassName}\n percentageClassName={percentageClassName}\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n />\n );\n};\n\nexport default ProgressBar;\n","import { ReactNode } from 'react';\nimport Modal from '../Modal/Modal';\nimport ProgressBar from '../ProgressBar/ProgressBar';\n\nexport interface ProgressModalProps {\n /** Controla se o modal está aberto */\n isOpen: boolean;\n /** Callback ao fechar */\n onClose: () => void;\n /**\n * Mensagem principal (vira o título do Modal / `<h2>` pra acessibilidade).\n * Quando ReactNode, use phrasing content (`<span>`, `<button>`, ícones).\n */\n message: ReactNode;\n /** URL ou import de imagem ilustrativa (opcional) */\n image?: string;\n /** Alt text da imagem — se não informado usa a `message` quando for string */\n imageAlt?: string;\n /**\n * Valor 0-100. Se omitido, mostra indicador indeterminado (pulse sem %).\n * Use quando o backend não reportar progresso real.\n */\n progress?: number;\n /** Tamanho do Modal. Default: 'sm' */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n}\n\n/**\n * Modal de progresso com imagem ilustrativa + título + ProgressBar.\n *\n * Pensado para operações assíncronas longas (upload, OCR, correção por IA, etc).\n * Quando `progress` é omitido, a barra é renderizada em modo indeterminado\n * (`animate-pulse` sem percentual visível).\n *\n * A `message` é usada como título do Modal (`<h2>`) pra garantir\n * acessibilidade — screen readers anunciam o dialog com esse texto.\n *\n * @example\n * ```tsx\n * <ProgressModal\n * isOpen={isLoading}\n * onClose={() => setIsLoading(false)}\n * image={myImage}\n * message=\"Analisando sua redação...\"\n * // progress opcional — sem valor, usa indicador indeterminado\n * />\n * ```\n */\nconst ProgressModal = ({\n isOpen,\n onClose,\n message,\n image,\n imageAlt,\n progress,\n size = 'sm',\n}: ProgressModalProps) => {\n const isIndeterminate = progress === undefined;\n const resolvedAlt =\n imageAlt ?? (typeof message === 'string' ? message : 'Carregando');\n\n return (\n <Modal isOpen={isOpen} onClose={onClose} title={message} size={size}>\n <div className=\"flex flex-col items-center gap-6 py-2\">\n {image && (\n <img\n src={image}\n alt={resolvedAlt}\n className=\"w-60 h-60 object-contain\"\n />\n )}\n\n <div className={isIndeterminate ? 'w-full animate-pulse' : 'w-full'}>\n <ProgressBar\n value={progress ?? 60}\n size=\"medium\"\n variant=\"blue\"\n showPercentage={!isIndeterminate}\n />\n </div>\n </div>\n </Modal>\n );\n};\n\nexport default ProgressModal;\n"],"mappings":"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","names":["jsx","jsxs","SIZE_CLASSES","jsx","jsx","jsxs","SIZE_CLASSES","jsx","jsxs"]}
1
+ {"version":3,"sources":["../../src/components/Modal/Modal.tsx","../../src/utils/utils.ts","../../src/components/Button/Button.tsx","../../src/components/Modal/utils/videoUtils.ts","../../src/components/Text/Text.tsx","../../src/components/ProgressBar/ProgressBar.tsx","../../src/components/ProgressModal/ProgressModal.tsx"],"sourcesContent":["import { ReactNode, useEffect, useId } from 'react';\nimport { X } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\nimport Button from '../Button/Button';\nimport {\n isYouTubeUrl,\n getYouTubeVideoId,\n getYouTubeEmbedUrl,\n} from './utils/videoUtils';\n\n/**\n * Lookup table for size classes\n */\nconst SIZE_CLASSES = {\n xs: 'max-w-[360px]',\n sm: 'max-w-[420px]',\n md: 'max-w-[510px]',\n lg: 'max-w-[640px]',\n xl: 'max-w-[970px]',\n} as const;\n\n/**\n * Modal component props interface\n */\ntype ModalProps = {\n contentClassName?: string;\n /** Whether the modal is open */\n isOpen: boolean;\n /** Function to close the modal */\n onClose: () => void;\n /**\n * Modal title. Aceita string OU ReactNode pra permitir elementos inline\n * (ex: botão de voltar + label). Como é renderizado dentro de <h2>,\n * quando ReactNode use APENAS phrasing content (`<span>`, `<button>`,\n * ícones) — nunca `<div>` ou `<p>`.\n */\n title: ReactNode;\n /** Modal description/content */\n children?: ReactNode;\n /** Size of the modal */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n /** Additional CSS classes for the modal content */\n className?: string;\n /** Whether pressing Escape should close the modal */\n closeOnEscape?: boolean;\n /** Footer content (typically buttons) */\n footer?: ReactNode;\n /** Hide the close button */\n hideCloseButton?: boolean;\n /** Modal variant */\n variant?: 'default' | 'activity';\n /** Description for activity variant */\n description?: string;\n /** Image URL for activity variant */\n image?: string;\n /** Alt text for activity image (leave empty for decorative images) */\n imageAlt?: string;\n /** Action link for activity variant */\n actionLink?: string;\n /** Action button label for activity variant */\n actionLabel?: string;\n};\n\n/**\n * Modal component for Analytica Ensino platforms\n *\n * A flexible modal component with multiple size variants and customizable behavior.\n *\n * @param isOpen - Whether the modal is currently open\n * @param onClose - Callback function called when the modal should be closed\n * @param title - The title displayed at the top of the modal\n * @param children - The main content of the modal\n * @param size - The size variant (xs, sm, md, lg, xl)\n * @param className - Additional CSS classes for the modal content\n * @param closeOnEscape - Whether pressing Escape closes the modal (default: true)\n * @param footer - Footer content, typically action buttons\n * @param hideCloseButton - Whether to hide the X close button (default: false)\n * @returns A modal overlay with content\n *\n * @example\n * ```tsx\n * <Modal\n * isOpen={isModalOpen}\n * onClose={() => setIsModalOpen(false)}\n * title=\"Invite your team\"\n * size=\"md\"\n * footer={\n * <div className=\"flex gap-3\">\n * <Button variant=\"outline\" onClick={() => setIsModalOpen(false)}>Cancel</Button>\n * <Button variant=\"solid\" onClick={handleExplore}>Explore</Button>\n * </div>\n * }\n * >\n * Elevate user interactions with our versatile modals.\n * </Modal>\n * ```\n */\nconst Modal = ({\n isOpen,\n onClose,\n title,\n children,\n size = 'md',\n className = '',\n closeOnEscape = true,\n footer,\n hideCloseButton = false,\n variant = 'default',\n description,\n image,\n imageAlt,\n actionLink,\n actionLabel,\n contentClassName = '',\n}: ModalProps) => {\n const titleId = useId();\n\n // Handle escape key\n useEffect(() => {\n if (!isOpen || !closeOnEscape) return;\n\n const handleEscape = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [isOpen, closeOnEscape, onClose]);\n\n // Handle body scroll lock and scrollbar shift fix\n useEffect(() => {\n if (!isOpen) return;\n\n // Calculate scrollbar width before hiding overflow\n const scrollbarWidth =\n window.innerWidth - document.documentElement.clientWidth;\n\n // Save original styles\n const originalOverflow = document.body.style.overflow;\n const originalPaddingRight = document.body.style.paddingRight;\n\n // Apply scroll lock\n document.body.style.overflow = 'hidden';\n\n // Fix scrollbar shift: add padding to compensate for lost scrollbar\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${scrollbarWidth}px`;\n\n // Create overlay to cover the padding area with backdrop color\n const overlay = document.createElement('div');\n overlay.id = 'modal-scrollbar-overlay';\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n right: 0;\n width: ${scrollbarWidth}px;\n height: 100vh;\n background-color: rgb(0 0 0 / 0.6);\n z-index: 40;\n pointer-events: none;\n `;\n document.body.appendChild(overlay);\n }\n\n return () => {\n // Restore original styles\n document.body.style.overflow = originalOverflow;\n document.body.style.paddingRight = originalPaddingRight;\n\n // Remove overlay\n const overlay = document.getElementById('modal-scrollbar-overlay');\n if (overlay) {\n overlay.remove();\n }\n };\n }, [isOpen]);\n\n if (!isOpen) return null;\n\n const sizeClasses = SIZE_CLASSES[size];\n const baseClasses =\n 'bg-secondary-50 rounded-3xl shadow-hard-shadow-2 border border-border-100 w-full mx-4';\n // Reset dialog default styles to prevent positioning issues\n const dialogResetClasses = 'p-0 m-0 border-none outline-none static';\n const modalClasses = cn(\n baseClasses,\n sizeClasses,\n dialogResetClasses,\n className\n );\n\n // Normalize URLs missing protocol\n const normalizeUrl = (href: string) =>\n /^https?:\\/\\//i.test(href) ? href : `https://${href}`;\n\n // Handle action link click\n const handleActionClick = () => {\n if (actionLink) {\n window.open(normalizeUrl(actionLink), '_blank', 'noopener,noreferrer');\n }\n };\n\n // Activity variant rendering\n if (variant === 'activity') {\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs border-none p-0 m-0 w-full cursor-default\">\n <dialog\n className={modalClasses}\n aria-labelledby={titleId}\n aria-modal=\"true\"\n open\n >\n {/* Header simples com X */}\n <div className=\"flex justify-end p-6 pb-0\">\n {!hideCloseButton && (\n <button\n onClick={onClose}\n className=\"p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2\"\n aria-label=\"Fechar modal\"\n >\n <X size={18} />\n </button>\n )}\n </div>\n\n {/* Conteúdo centralizado */}\n <div className=\"flex flex-col items-center px-6 pb-6 gap-5\">\n {/* Imagem ilustrativa */}\n {image && (\n <div className=\"flex justify-center\">\n <img\n src={image}\n alt={imageAlt ?? ''}\n className=\"w-[122px] h-[122px] object-contain\"\n />\n </div>\n )}\n\n {/* Título */}\n <h2\n id={titleId}\n className=\"text-lg font-semibold text-text-950 text-center\"\n >\n {title}\n </h2>\n\n {/* Descrição */}\n {description && (\n <p className=\"text-sm font-normal text-text-400 text-center max-w-md leading-[21px]\">\n {description}\n </p>\n )}\n\n {/* Ação: Botão ou Vídeo Embedado */}\n {actionLink && (\n <div className=\"w-full\">\n {(() => {\n const normalized = normalizeUrl(actionLink);\n const isYT = isYouTubeUrl(normalized);\n if (!isYT) return null;\n const id = getYouTubeVideoId(normalized);\n if (!id) {\n return (\n <Button\n variant=\"solid\"\n action=\"primary\"\n size=\"large\"\n className=\"w-full\"\n onClick={handleActionClick}\n >\n {actionLabel || 'Iniciar Atividade'}\n </Button>\n );\n }\n return (\n <iframe\n src={getYouTubeEmbedUrl(id)}\n className=\"w-full aspect-video rounded-lg\"\n allowFullScreen\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n title=\"Vídeo YouTube\"\n />\n );\n })()}\n {!isYouTubeUrl(normalizeUrl(actionLink)) && (\n <Button\n variant=\"solid\"\n action=\"primary\"\n size=\"large\"\n className=\"w-full\"\n onClick={handleActionClick}\n >\n {actionLabel || 'Iniciar Atividade'}\n </Button>\n )}\n </div>\n )}\n </div>\n </dialog>\n </div>\n );\n }\n\n // Default variant rendering\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-xs border-none p-0 m-0 w-full cursor-default\">\n <dialog\n className={modalClasses}\n aria-labelledby={titleId}\n aria-modal=\"true\"\n open\n >\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-6\">\n <h2 id={titleId} className=\"text-lg font-semibold text-text-950\">\n {title}\n </h2>\n {!hideCloseButton && (\n <button\n onClick={onClose}\n className=\"p-1 text-text-500 hover:text-text-700 hover:bg-background-50 rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-indicator-info focus:ring-offset-2\"\n aria-label=\"Fechar modal\"\n >\n <X size={18} />\n </button>\n )}\n </div>\n\n {/* Content */}\n {children && (\n <div className={cn('px-6 pb-6', contentClassName)}>\n <div\n className={cn(\n 'text-text-500 font-normal text-sm leading-6',\n contentClassName?.includes('flex') &&\n 'flex flex-col flex-1 min-h-0'\n )}\n >\n {children}\n </div>\n </div>\n )}\n\n {/* Footer */}\n {footer && (\n <div className=\"flex justify-end gap-3 px-6 pb-6\">{footer}</div>\n )}\n </dialog>\n </div>\n );\n};\n\nexport default Modal;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Lookup table for variant and action class combinations\n */\nconst VARIANT_ACTION_CLASSES = {\n solid: {\n primary:\n 'bg-primary-950 text-text border border-primary-950 hover:bg-primary-800 hover:border-primary-800 focus-visible:outline-none focus-visible:bg-primary-950 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-primary-700 active:border-primary-700 disabled:bg-primary-500 disabled:border-primary-500 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-text-950 text-text border border-text-800 hover:bg-text-800 hover:border-text-950 focus-visible:outline-none focus-visible:bg-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-text-700 active:border-text-700 disabled:bg-text-500 disabled:border-text-500 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-success-500 text-text border border-success-500 hover:bg-success-600 hover:border-success-600 focus-visible:outline-none focus-visible:bg-success-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-success-700 active:border-success-700 disabled:bg-success-500 disabled:border-success-500 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-error-500 text-text border border-error-500 hover:bg-error-600 hover:border-error-600 focus-visible:outline-none focus-visible:bg-error-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-error-700 active:border-error-700 disabled:bg-error-500 disabled:border-error-500 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n outline: {\n primary:\n 'bg-transparent text-primary-950 border border-primary-950 hover:bg-background-50 hover:text-primary-400 hover:border-primary-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 active:border-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-transparent text-text-950 border border-text-800 hover:bg-background-50 hover:text-text-700 hover:border-text-700 focus-visible:border-0 focus-visible:outline-none focus-visible:text-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-text-700 active:border-text-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 border border-success-300 hover:bg-background-50 hover:text-success-400 hover:border-success-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 active:border-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 border border-error-300 hover:bg-background-50 hover:text-error-400 hover:border-error-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 active:border-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n link: {\n primary:\n 'bg-transparent text-primary-950 hover:text-primary-400 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-transparent text-text-950 hover:text-text-800 focus-visible:outline-none focus-visible:text-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-text-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 hover:text-success-400 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 hover:text-error-400 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n} as const;\n\n/**\n * Lookup table for size classes\n */\nconst SIZE_CLASSES = {\n 'extra-small': 'text-xs px-3.5 py-2',\n small: 'text-sm px-4 py-2.5',\n medium: 'text-md px-5 py-2.5',\n large: 'text-lg px-6 py-3',\n 'extra-large': 'text-lg px-7 py-3.5',\n} as const;\n\n/**\n * Button component props interface\n */\ntype ButtonProps = {\n /** Content to be displayed inside the button */\n children: ReactNode;\n /** Ícone à esquerda do texto */\n iconLeft?: ReactNode;\n /** Ícone à direita do texto */\n iconRight?: ReactNode;\n /** Size of the button */\n size?: 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large';\n /** Visual variant of the button. Use 'raw' for no default styling */\n variant?: 'solid' | 'outline' | 'link' | 'raw';\n /** Action type of the button */\n action?: 'primary' | 'secondary' | 'positive' | 'negative';\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * Button component for Analytica Ensino platforms\n *\n * A flexible button component with multiple variants, sizes and actions.\n *\n * @param children - The content to display inside the button\n * @param size - The size variant (extra-small, small, medium, large, extra-large)\n * @param variant - The visual style variant (solid, outline, link)\n * @param action - The action type (primary, secondary, positive, negative)\n * @param className - Additional CSS classes\n * @param props - All other standard button HTML attributes\n * @returns A styled button element\n *\n * @example\n * ```tsx\n * <Button variant=\"solid\" action=\"primary\" size=\"medium\" onClick={() => console.log('clicked')}>\n * Click me\n * </Button>\n * ```\n */\nconst Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n children,\n iconLeft,\n iconRight,\n size = 'medium',\n variant = 'solid',\n action = 'primary',\n className = '',\n disabled,\n type = 'button',\n ...props\n },\n ref\n ) => {\n // Raw variant: no default styling, only className\n if (variant === 'raw') {\n return (\n <button\n ref={ref}\n className={className}\n disabled={disabled}\n type={type}\n {...props}\n >\n {iconLeft && (\n <span className=\"mr-2 flex items-center\">{iconLeft}</span>\n )}\n {children}\n {iconRight && (\n <span className=\"ml-2 flex items-center\">{iconRight}</span>\n )}\n </button>\n );\n }\n\n // Get classes from lookup tables\n const sizeClasses = SIZE_CLASSES[size];\n const variantClasses = VARIANT_ACTION_CLASSES[variant][action];\n\n const baseClasses =\n 'inline-flex items-center justify-center rounded-full cursor-pointer font-medium';\n\n return (\n <button\n ref={ref}\n className={cn(baseClasses, variantClasses, sizeClasses, className)}\n disabled={disabled}\n type={type}\n {...props}\n >\n {iconLeft && <span className=\"mr-2 flex items-center\">{iconLeft}</span>}\n {children}\n {iconRight && (\n <span className=\"ml-2 flex items-center\">{iconRight}</span>\n )}\n </button>\n );\n }\n);\n\nButton.displayName = 'Button';\n\nexport default Button;\n","/**\n * Video utilities for Modal component\n *\n * Utilities to handle YouTube video embedding and URL detection\n */\n\n/**\n * Check if a given URL is a YouTube URL\n *\n * @param url - The URL to check\n * @returns true if the URL is from YouTube, false otherwise\n */\nexport const isYouTubeUrl = (url: string): boolean => {\n const youtubeRegex =\n /^(https?:\\/\\/)?((www|m|music)\\.)?(youtube\\.com|youtu\\.be|youtube-nocookie\\.com)\\/.+/i;\n return youtubeRegex.test(url);\n};\n\n/**\n * Validate if hostname is a legitimate YouTube host\n *\n * @param host - The hostname to validate\n * @returns The type of YouTube host or null if invalid\n */\nconst isValidYouTubeHost = (\n host: string\n): 'youtu.be' | 'youtube' | 'nocookie' | null => {\n if (host === 'youtu.be') return 'youtu.be';\n\n const isValidYouTubeCom =\n host === 'youtube.com' ||\n (host.endsWith('.youtube.com') &&\n /^(www|m|music)\\.youtube\\.com$/.test(host));\n\n if (isValidYouTubeCom) return 'youtube';\n\n const isValidNoCookie =\n host === 'youtube-nocookie.com' ||\n (host.endsWith('.youtube-nocookie.com') &&\n /^(www|m|music)\\.youtube-nocookie\\.com$/.test(host));\n\n if (isValidNoCookie) return 'nocookie';\n\n return null;\n};\n\n/**\n * Extract video ID from youtu.be path\n *\n * @param pathname - The URL pathname\n * @returns The video ID or null\n */\nconst extractYoutuBeId = (pathname: string): string | null => {\n const firstSeg = pathname.split('/').filter(Boolean)[0];\n return firstSeg || null;\n};\n\n/**\n * Extract video ID from YouTube or nocookie domains\n *\n * @param pathname - The URL pathname\n * @param searchParams - The URL search parameters\n * @returns The video ID or null\n */\nconst extractYouTubeId = (\n pathname: string,\n searchParams: URLSearchParams\n): string | null => {\n const parts = pathname.split('/').filter(Boolean);\n const [first, second] = parts;\n\n if (first === 'embed' && second) return second;\n if (first === 'shorts' && second) return second;\n if (first === 'live' && second) return second;\n\n const v = searchParams.get('v');\n if (v) return v;\n\n return null;\n};\n\n/**\n * Extract YouTube video ID from URL\n *\n * @param url - The YouTube URL\n * @returns The video ID if found, null otherwise\n */\nexport const getYouTubeVideoId = (url: string): string | null => {\n try {\n const u = new URL(url);\n const hostType = isValidYouTubeHost(u.hostname.toLowerCase());\n\n if (!hostType) return null;\n\n if (hostType === 'youtu.be') {\n return extractYoutuBeId(u.pathname);\n }\n\n return extractYouTubeId(u.pathname, u.searchParams);\n } catch {\n return null;\n }\n};\n\n/**\n * Generate YouTube embed URL for iframe\n *\n * @param videoId - The YouTube video ID\n * @returns The embed URL for the video\n */\nexport const getYouTubeEmbedUrl = (videoId: string): string => {\n return `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=0&rel=0&modestbranding=1`;\n};\n","import { ComponentPropsWithoutRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithoutRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses: string;\n let weightClasses: string;\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n","import { ReactNode } from 'react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Progress bar size variants\n */\ntype ProgressBarSize = 'small' | 'medium';\n\n/**\n * Progress bar color variants\n */\ntype ProgressBarVariant = 'blue' | 'green';\n\n/**\n * Progress bar layout variants\n */\ntype ProgressBarLayout = 'default' | 'stacked' | 'compact';\n\n/**\n * Size configurations using Tailwind classes\n */\nconst SIZE_CLASSES = {\n small: {\n container: 'h-1', // 4px height (h-1 = 4px in Tailwind)\n bar: 'h-1', // 4px height for the fill bar\n spacing: 'gap-2', // 8px gap between label and progress bar\n layout: 'flex-col', // vertical layout for small\n borderRadius: 'rounded-full', // 9999px border radius\n },\n medium: {\n container: 'h-2', // 8px height (h-2 = 8px in Tailwind)\n bar: 'h-2', // 8px height for the fill bar\n spacing: 'gap-2', // 8px gap between progress bar and label\n layout: 'flex-row items-center', // horizontal layout for medium\n borderRadius: 'rounded-lg', // 8px border radius\n },\n} as const;\n\n/**\n * Color configurations using design system colors\n */\nconst VARIANT_CLASSES = {\n blue: {\n background: 'bg-background-300', // Background track color (#D5D4D4)\n fill: 'bg-primary-700', // Blue for activity progress (#2271C4)\n },\n green: {\n background: 'bg-background-300', // Background track color (#D5D4D4)\n fill: 'bg-success-200', // Green for performance (#84D3A2)\n },\n} as const;\n\n/**\n * Type for size classes\n */\ntype SizeClassType = (typeof SIZE_CLASSES)[keyof typeof SIZE_CLASSES];\n\n/**\n * Type for variant classes\n */\ntype VariantClassType = (typeof VARIANT_CLASSES)[keyof typeof VARIANT_CLASSES];\n\n/**\n * Common props shared across all layout components\n */\ninterface BaseLayoutProps {\n className: string;\n label: ReactNode;\n showPercentage: boolean;\n showHitCount: boolean;\n labelClassName: string;\n percentageClassName: string;\n clampedValue: number;\n max: number;\n percentage: number;\n variantClasses: VariantClassType;\n}\n\n/**\n * Dimensions configuration for layouts\n */\ninterface LayoutDimensions {\n width: string;\n height: string;\n}\n\n/**\n * Props for StackedLayout component\n */\ninterface StackedLayoutProps extends BaseLayoutProps {\n dimensions: LayoutDimensions;\n}\n\n/**\n * Props for CompactLayout component\n */\ninterface CompactLayoutProps extends BaseLayoutProps {\n dimensions: LayoutDimensions;\n}\n\n/**\n * Props for DefaultLayout component\n */\ninterface DefaultLayoutProps {\n className: string;\n size: ProgressBarSize;\n sizeClasses: SizeClassType;\n variantClasses: VariantClassType;\n label: ReactNode;\n showPercentage: boolean;\n labelClassName: string;\n percentageClassName: string;\n clampedValue: number;\n max: number;\n percentage: number;\n}\n\n/**\n * ProgressBar component props interface\n */\nexport type ProgressBarProps = {\n /** Progress value between 0 and 100 */\n value: number;\n /** Maximum value (defaults to 100) */\n max?: number;\n /** Size variant of the progress bar */\n size?: ProgressBarSize;\n /** Color variant of the progress bar */\n variant?: ProgressBarVariant;\n /** Layout variant of the progress bar */\n layout?: ProgressBarLayout;\n /** Optional label to display */\n label?: ReactNode;\n /** Show percentage text */\n showPercentage?: boolean;\n /**\n * Show hit count (e.g., \"28 de 30\") instead of percentage\n *\n * PRIORITY: When both showHitCount and showPercentage are true,\n * showHitCount takes precedence (stacked and compact layouts only).\n * Default layout does not support showHitCount.\n */\n showHitCount?: boolean;\n /** Additional CSS classes */\n className?: string;\n /** Label CSS classes */\n labelClassName?: string;\n /** Percentage text CSS classes */\n percentageClassName?: string;\n /** Custom width for stacked layout (defaults to w-[380px]) */\n stackedWidth?: string;\n /** Custom height for stacked layout (defaults to h-[35px]) */\n stackedHeight?: string;\n /** Custom width for compact layout (defaults to w-[131px]) */\n compactWidth?: string;\n /** Custom height for compact layout (defaults to h-[24px]) */\n compactHeight?: string;\n};\n\n/**\n * Helper function to calculate safe progress values\n */\nconst calculateProgressValues = (value: number, max: number) => {\n const safeValue = isNaN(value) ? 0 : value;\n const clampedValue = Math.max(0, Math.min(safeValue, max));\n const percentage = max === 0 ? 0 : (clampedValue / max) * 100;\n\n return { clampedValue, percentage };\n};\n\n/**\n * Helper function to determine if header content should be shown\n */\nconst shouldShowHeader = (\n label: ReactNode,\n showPercentage: boolean,\n showHitCount: boolean\n): boolean => {\n return !!(label || showPercentage || showHitCount);\n};\n\n/**\n * Centralized function to determine display priority and content\n *\n * PRIORITY ORDER (consistent across all layouts):\n * 1. showHitCount (highest priority) - displays \"X de Y\" format\n * 2. showPercentage - displays \"X%\" format\n * 3. label (lowest priority) - displays custom label\n *\n * @param showHitCount - Whether to show hit count format\n * @param showPercentage - Whether to show percentage format\n * @param label - Custom label to display\n * @param clampedValue - Current progress value\n * @param max - Maximum progress value\n * @param percentage - Calculated percentage value\n * @returns Object with content type and formatted content\n */\nconst getDisplayPriority = (\n showHitCount: boolean,\n showPercentage: boolean,\n label: ReactNode,\n clampedValue: number,\n max: number,\n percentage: number\n) => {\n if (showHitCount) {\n return {\n type: 'hitCount' as const,\n content: `${Math.round(clampedValue)} de ${max}`,\n hasMetrics: true,\n };\n }\n\n if (showPercentage) {\n return {\n type: 'percentage' as const,\n content: `${Math.round(percentage)}%`,\n hasMetrics: true,\n };\n }\n\n return {\n type: 'label' as const,\n content: label,\n hasMetrics: false,\n };\n};\n\n/**\n * Parameters for compact layout configuration\n */\ninterface CompactLayoutConfigParams {\n showPercentage: boolean;\n showHitCount: boolean;\n percentage: number;\n clampedValue: number;\n max: number;\n label: ReactNode;\n percentageClassName: string;\n labelClassName: string;\n}\n\n/**\n * Helper function to get compact layout configuration\n *\n * PRIORITY ORDER (consistent across all layouts):\n * 1. showHitCount (highest priority) - displays \"X de Y\" format\n * 2. showPercentage - displays \"X%\" format\n * 3. label (lowest priority) - displays custom label\n *\n * When both showHitCount and showPercentage are true, showHitCount takes precedence.\n */\nconst getCompactLayoutConfig = ({\n showPercentage,\n showHitCount,\n percentage,\n clampedValue,\n max,\n label,\n percentageClassName,\n labelClassName,\n}: CompactLayoutConfigParams) => {\n // Use centralized priority logic for consistency\n const displayPriority = getDisplayPriority(\n showHitCount,\n showPercentage,\n label,\n clampedValue,\n max,\n percentage\n );\n\n return {\n color: displayPriority.hasMetrics ? 'text-primary-600' : 'text-primary-700',\n className: displayPriority.hasMetrics\n ? percentageClassName\n : labelClassName,\n content: displayPriority.content,\n };\n};\n\n/**\n * Helper function to get default layout display configuration\n *\n * PRIORITY ORDER for default layout (showHitCount is not supported):\n * 1. showPercentage (when enabled, takes precedence over label)\n * 2. label (shown only when showPercentage is false)\n *\n * Note: Default layout does not support showHitCount feature.\n */\nconst getDefaultLayoutDisplayConfig = (\n size: ProgressBarSize,\n label: ReactNode,\n showPercentage: boolean\n) => ({\n showHeader: size === 'small' && !!(label || showPercentage),\n showPercentage: size === 'medium' && showPercentage,\n showLabel: size === 'medium' && !!label && !showPercentage, // Only show label when percentage is not shown\n});\n\n/**\n * Helper function to render hit count or percentage display for stacked layout\n *\n * PRIORITY ORDER (consistent across all layouts):\n * 1. showHitCount (highest priority) - displays \"X de Y\" format\n * 2. showPercentage - displays \"X%\" format\n *\n * When both showHitCount and showPercentage are true, showHitCount takes precedence.\n */\nconst renderStackedHitCountDisplay = (\n showHitCount: boolean,\n showPercentage: boolean,\n clampedValue: number,\n max: number,\n percentage: number,\n percentageClassName: string\n): ReactNode => {\n if (!showHitCount && !showPercentage) return null;\n\n // Use centralized priority logic for consistency\n const displayPriority = getDisplayPriority(\n showHitCount,\n showPercentage,\n null, // label is not relevant for stacked layout metrics display\n clampedValue,\n max,\n percentage\n );\n\n return (\n <div\n className={cn(\n 'text-xs font-medium leading-[14px] text-right',\n percentageClassName\n )}\n >\n {displayPriority.type === 'hitCount' ? (\n <>\n <span className=\"text-success-200\">{Math.round(clampedValue)}</span>\n <span className=\"text-text-600\"> de {max}</span>\n </>\n ) : (\n <Text size=\"xs\" weight=\"medium\" className=\"text-success-200\">\n {Math.round(percentage)}%\n </Text>\n )}\n </div>\n );\n};\n\n/**\n * Base progress bar component with common rendering logic\n */\nconst ProgressBarBase = ({\n clampedValue,\n max,\n percentage,\n label,\n variantClasses,\n containerClassName,\n fillClassName,\n}: {\n clampedValue: number;\n max: number;\n percentage: number;\n label: ReactNode;\n variantClasses: VariantClassType;\n containerClassName: string;\n fillClassName: string;\n}) => (\n <div\n className={cn(\n containerClassName,\n variantClasses.background,\n 'overflow-hidden relative'\n )}\n >\n <progress\n value={clampedValue}\n max={max}\n aria-label={\n typeof label === 'string'\n ? `${label}: ${Math.round(percentage)}% complete`\n : `Progress: ${Math.round(percentage)}% of ${max}`\n }\n className=\"absolute inset-0 w-full h-full opacity-0\"\n />\n <div\n className={cn(\n fillClassName,\n variantClasses.fill,\n 'transition-all duration-300 ease-out'\n )}\n style={{ width: `${percentage}%` }}\n />\n </div>\n);\n\n/**\n * Stacked layout component\n */\nconst StackedLayout = ({\n className,\n label,\n showPercentage,\n showHitCount,\n labelClassName,\n percentageClassName,\n clampedValue,\n max,\n percentage,\n variantClasses,\n dimensions,\n}: StackedLayoutProps) => (\n <div\n className={cn(\n 'flex flex-col items-start gap-2',\n dimensions.width,\n dimensions.height,\n className\n )}\n >\n {shouldShowHeader(label, showPercentage, showHitCount) && (\n <div className=\"flex flex-row justify-between items-center w-full h-[19px]\">\n {label && (\n <Text\n as=\"div\"\n size=\"md\"\n weight=\"medium\"\n className={cn('text-text-600 leading-[19px]', labelClassName)}\n >\n {label}\n </Text>\n )}\n\n {renderStackedHitCountDisplay(\n showHitCount,\n showPercentage,\n clampedValue,\n max,\n percentage,\n percentageClassName\n )}\n </div>\n )}\n\n <ProgressBarBase\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n label={label}\n variantClasses={variantClasses}\n containerClassName=\"w-full h-2 rounded-lg\"\n fillClassName=\"h-2 rounded-lg shadow-hard-shadow-3\"\n />\n </div>\n);\n\n/**\n * Compact layout component\n */\nconst CompactLayout = ({\n className,\n label,\n showPercentage,\n showHitCount,\n labelClassName,\n percentageClassName,\n clampedValue,\n max,\n percentage,\n variantClasses,\n dimensions,\n}: CompactLayoutProps) => {\n const {\n color,\n className: compactClassName,\n content,\n } = getCompactLayoutConfig({\n showPercentage,\n showHitCount,\n percentage,\n clampedValue,\n max,\n label,\n percentageClassName,\n labelClassName,\n });\n\n return (\n <div\n className={cn(\n 'flex flex-col items-start gap-1',\n dimensions.width,\n dimensions.height,\n className\n )}\n >\n {shouldShowHeader(label, showPercentage, showHitCount) && (\n <Text\n as=\"div\"\n size=\"sm\"\n weight=\"medium\"\n color={color}\n className={cn('leading-4 w-full', compactClassName)}\n >\n {content}\n </Text>\n )}\n\n <ProgressBarBase\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n label={label}\n variantClasses={variantClasses}\n containerClassName=\"w-full h-1 rounded-full\"\n fillClassName=\"h-1 rounded-full\"\n />\n </div>\n );\n};\n\n/**\n * Default layout component\n */\nconst DefaultLayout = ({\n className,\n size,\n sizeClasses,\n variantClasses,\n label,\n showPercentage,\n labelClassName,\n percentageClassName,\n clampedValue,\n max,\n percentage,\n}: DefaultLayoutProps) => {\n const gapClass = size === 'medium' ? 'gap-2' : sizeClasses.spacing;\n const progressBarClass = size === 'medium' ? 'flex-grow' : 'w-full';\n const displayConfig = getDefaultLayoutDisplayConfig(\n size,\n label,\n showPercentage\n );\n\n return (\n <div className={cn('flex', sizeClasses.layout, gapClass, className)}>\n {displayConfig.showHeader && (\n <div className=\"flex flex-row items-center justify-between w-full\">\n {label && (\n <Text\n as=\"div\"\n size=\"xs\"\n weight=\"medium\"\n className={cn(\n 'text-text-950 leading-none tracking-normal text-center',\n labelClassName\n )}\n >\n {label}\n </Text>\n )}\n\n {showPercentage && (\n <Text\n size=\"xs\"\n weight=\"medium\"\n className={cn(\n 'text-text-950 leading-none tracking-normal text-center',\n percentageClassName\n )}\n >\n {Math.round(percentage)}%\n </Text>\n )}\n </div>\n )}\n\n <ProgressBarBase\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n label={label}\n variantClasses={variantClasses}\n containerClassName={cn(\n progressBarClass,\n sizeClasses.container,\n sizeClasses.borderRadius\n )}\n fillClassName={cn(\n sizeClasses.bar,\n sizeClasses.borderRadius,\n 'shadow-hard-shadow-3'\n )}\n />\n\n {displayConfig.showPercentage && (\n <Text\n size=\"xs\"\n weight=\"medium\"\n className={cn(\n 'text-text-950 leading-none tracking-normal text-center flex-none',\n percentageClassName\n )}\n >\n {Math.round(percentage)}%\n </Text>\n )}\n\n {displayConfig.showLabel && (\n <Text\n as=\"div\"\n size=\"xs\"\n weight=\"medium\"\n className={cn(\n 'text-text-950 leading-none tracking-normal text-center flex-none',\n labelClassName\n )}\n >\n {label}\n </Text>\n )}\n </div>\n );\n};\n\n/**\n * ProgressBar component for Analytica Ensino platforms\n *\n * A progress bar component with size and color variants designed for tracking\n * activity progress (blue) and performance metrics (green).\n * Uses the Analytica Ensino Design System colors from styles.css with automatic\n * light/dark mode support. Includes Text component integration for consistent typography.\n *\n * CONTENT DISPLAY PRIORITY (Consistent across all layouts):\n * 1. showHitCount (highest) - \"X de Y\" format (stacked/compact only)\n * 2. showPercentage - \"X%\" format\n * 3. label (lowest) - Custom label text\n *\n * When multiple display options are enabled, higher priority options take precedence.\n *\n * @example\n * ```tsx\n * // Basic progress bar\n * <ProgressBar value={65} />\n *\n * // Activity progress (blue)\n * <ProgressBar variant=\"blue\" value={45} label=\"Progress\" showPercentage />\n *\n * // Performance metrics (green)\n * <ProgressBar variant=\"green\" size=\"medium\" value={85} label=\"Performance\" />\n *\n * // Small size with custom max value\n * <ProgressBar size=\"small\" value={3} max={5} showPercentage />\n *\n * // Stacked layout with fixed width and hit count\n * <ProgressBar layout=\"stacked\" variant=\"green\" value={28} max={30} label=\"Fáceis\" showHitCount />\n *\n * // Compact layout for small cards\n * <ProgressBar layout=\"compact\" variant=\"blue\" value={70} label=\"Questão 08\" />\n * ```\n */\nconst ProgressBar = ({\n value,\n max = 100,\n size = 'medium',\n variant = 'blue',\n layout = 'default',\n label,\n showPercentage = false,\n showHitCount = false,\n className = '',\n labelClassName = '',\n percentageClassName = '',\n stackedWidth,\n stackedHeight,\n compactWidth,\n compactHeight,\n}: ProgressBarProps) => {\n const { clampedValue, percentage } = calculateProgressValues(value, max);\n const sizeClasses = SIZE_CLASSES[size];\n const variantClasses = VARIANT_CLASSES[variant];\n\n if (layout === 'stacked') {\n return (\n <StackedLayout\n className={className}\n label={label}\n showPercentage={showPercentage}\n showHitCount={showHitCount}\n labelClassName={labelClassName}\n percentageClassName={percentageClassName}\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n variantClasses={variantClasses}\n dimensions={{\n width: stackedWidth ?? 'w-[380px]',\n height: stackedHeight ?? 'h-[35px]',\n }}\n />\n );\n }\n\n if (layout === 'compact') {\n return (\n <CompactLayout\n className={className}\n label={label}\n showPercentage={showPercentage}\n showHitCount={showHitCount}\n labelClassName={labelClassName}\n percentageClassName={percentageClassName}\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n variantClasses={variantClasses}\n dimensions={{\n width: compactWidth ?? 'w-[131px]',\n height: compactHeight ?? 'h-[24px]',\n }}\n />\n );\n }\n\n return (\n <DefaultLayout\n className={className}\n size={size}\n sizeClasses={sizeClasses}\n variantClasses={variantClasses}\n label={label}\n showPercentage={showPercentage}\n labelClassName={labelClassName}\n percentageClassName={percentageClassName}\n clampedValue={clampedValue}\n max={max}\n percentage={percentage}\n />\n );\n};\n\nexport default ProgressBar;\n","import { ReactNode } from 'react';\nimport Modal from '../Modal/Modal';\nimport ProgressBar from '../ProgressBar/ProgressBar';\nimport Text from '../Text/Text';\n\nexport interface ProgressModalProps {\n /** Controla se o modal está aberto */\n isOpen: boolean;\n /** Callback ao fechar */\n onClose: () => void;\n /**\n * Mensagem principal exibida abaixo da imagem. Também é usada como\n * accessible name do dialog (renderizada visualmente escondida no `<h2>`\n * do Modal pra leitores de tela).\n */\n message: ReactNode;\n /** URL ou import de imagem ilustrativa (opcional) */\n image?: string;\n /** Alt text da imagem — se não informado usa a `message` quando for string */\n imageAlt?: string;\n /**\n * Accessible name quando `message` é ReactNode. Se omitido, cai em\n * \"Carregando\". Ignorado quando `message` é string.\n */\n accessibleName?: string;\n /**\n * Valor 0-100. Se omitido, mostra indicador indeterminado (pulse sem %).\n * Use quando o backend não reportar progresso real.\n */\n progress?: number;\n /** Tamanho do Modal. Default: 'sm' */\n size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';\n}\n\n/**\n * Modal de progresso com imagem ilustrativa + mensagem + ProgressBar.\n *\n * Pensado para operações assíncronas longas (upload, OCR, correção por IA, etc).\n * Quando `progress` é omitido, a barra é renderizada em modo indeterminado\n * (`animate-pulse` sem percentual visível).\n *\n * Layout: X no topo direito, imagem centralizada, mensagem abaixo da imagem,\n * progress bar ao final. A mensagem também é injetada como título invisível\n * (`sr-only`) no `<h2>` do Modal pra acessibilidade.\n *\n * @example\n * ```tsx\n * <ProgressModal\n * isOpen={isLoading}\n * onClose={() => setIsLoading(false)}\n * image={myImage}\n * message=\"Analisando sua redação...\"\n * // progress opcional — sem valor, usa indicador indeterminado\n * />\n * ```\n */\nconst ProgressModal = ({\n isOpen,\n onClose,\n message,\n image,\n imageAlt,\n accessibleName,\n progress,\n size = 'sm',\n}: ProgressModalProps) => {\n const isIndeterminate = progress === undefined;\n const resolvedAccessibleName =\n typeof message === 'string' ? message : (accessibleName ?? 'Carregando');\n const resolvedAlt = imageAlt ?? resolvedAccessibleName;\n\n return (\n <Modal\n isOpen={isOpen}\n onClose={onClose}\n title={<span className=\"sr-only\">{resolvedAccessibleName}</span>}\n size={size}\n >\n <div className=\"flex flex-col items-center gap-6 py-2\">\n {image && (\n <img\n src={image}\n alt={resolvedAlt}\n className=\"w-60 h-60 object-contain\"\n />\n )}\n\n <Text size=\"md\" className=\"text-text-950 text-center\">\n {message}\n </Text>\n\n <div className={isIndeterminate ? 'w-full animate-pulse' : 'w-full'}>\n <ProgressBar\n value={progress ?? 60}\n size=\"medium\"\n variant=\"blue\"\n showPercentage={!isIndeterminate}\n />\n </div>\n </div>\n </Modal>\n );\n};\n\nexport default ProgressModal;\n"],"mappings":"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","names":["jsx","jsxs","SIZE_CLASSES","jsx","jsx","jsxs","SIZE_CLASSES","jsx","jsxs"]}
package/dist/index.js CHANGED
@@ -16980,6 +16980,7 @@ var loadingModal_default = LoadingModal;
16980
16980
 
16981
16981
  // src/components/ProgressModal/ProgressModal.tsx
16982
16982
  init_Modal();
16983
+ init_Text();
16983
16984
  var import_jsx_runtime75 = require("react/jsx-runtime");
16984
16985
  var ProgressModal = ({
16985
16986
  isOpen,
@@ -16987,30 +16988,42 @@ var ProgressModal = ({
16987
16988
  message,
16988
16989
  image,
16989
16990
  imageAlt,
16991
+ accessibleName,
16990
16992
  progress,
16991
16993
  size = "sm"
16992
16994
  }) => {
16993
16995
  const isIndeterminate = progress === void 0;
16994
- const resolvedAlt = imageAlt ?? (typeof message === "string" ? message : "Carregando");
16995
- return /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Modal_default, { isOpen, onClose, title: message, size, children: /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "flex flex-col items-center gap-6 py-2", children: [
16996
- image && /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
16997
- "img",
16998
- {
16999
- src: image,
17000
- alt: resolvedAlt,
17001
- className: "w-60 h-60 object-contain"
17002
- }
17003
- ),
17004
- /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: isIndeterminate ? "w-full animate-pulse" : "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
17005
- ProgressBar_default,
17006
- {
17007
- value: progress ?? 60,
17008
- size: "medium",
17009
- variant: "blue",
17010
- showPercentage: !isIndeterminate
17011
- }
17012
- ) })
17013
- ] }) });
16996
+ const resolvedAccessibleName = typeof message === "string" ? message : accessibleName ?? "Carregando";
16997
+ const resolvedAlt = imageAlt ?? resolvedAccessibleName;
16998
+ return /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
16999
+ Modal_default,
17000
+ {
17001
+ isOpen,
17002
+ onClose,
17003
+ title: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "sr-only", children: resolvedAccessibleName }),
17004
+ size,
17005
+ children: /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("div", { className: "flex flex-col items-center gap-6 py-2", children: [
17006
+ image && /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
17007
+ "img",
17008
+ {
17009
+ src: image,
17010
+ alt: resolvedAlt,
17011
+ className: "w-60 h-60 object-contain"
17012
+ }
17013
+ ),
17014
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Text_default, { size: "md", className: "text-text-950 text-center", children: message }),
17015
+ /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("div", { className: isIndeterminate ? "w-full animate-pulse" : "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
17016
+ ProgressBar_default,
17017
+ {
17018
+ value: progress ?? 60,
17019
+ size: "medium",
17020
+ variant: "blue",
17021
+ showPercentage: !isIndeterminate
17022
+ }
17023
+ ) })
17024
+ ] })
17025
+ }
17026
+ );
17014
17027
  };
17015
17028
  var ProgressModal_default = ProgressModal;
17016
17029