{"version":3,"sources":["../../src/components/Whiteboard/Whiteboard.tsx","../../src/utils/utils.ts"],"sourcesContent":["import { HTMLAttributes, useCallback, useState } from 'react';\nimport { ArrowsOut } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\n// Design constants for critical layout dimensions\nconst IMAGE_WIDTH = 225;\nconst IMAGE_HEIGHT = 90;\n\n/**\n * Whiteboard image item interface\n */\nexport interface WhiteboardImage {\n  id: string;\n  imageUrl: string;\n  title?: string;\n}\n\n/**\n * Whiteboard component props interface\n */\nexport interface WhiteboardProps extends HTMLAttributes<HTMLDivElement> {\n  /** Array of images to display in the whiteboard */\n  images: WhiteboardImage[];\n  /** Whether to show download button on images */\n  showDownload?: boolean;\n  /** Custom className for the container */\n  className?: string;\n  /** Callback when download button is clicked */\n  onDownload?: (image: WhiteboardImage) => void;\n  /** Maximum number of images to display per row on desktop */\n  imagesPerRow?: 2 | 3 | 4;\n}\n\n/**\n * Whiteboard component for displaying classroom board images\n * @param props Component properties\n * @returns Whiteboard component\n */\nconst Whiteboard = ({\n  images,\n  showDownload = true,\n  className,\n  onDownload,\n  imagesPerRow = 2,\n  ...rest\n}: WhiteboardProps) => {\n  // State to track images that failed to load\n  const [imageErrors, setImageErrors] = useState<Set<string>>(new Set());\n\n  /**\n   * Handle image download\n   */\n  const handleDownload = useCallback(\n    (image: WhiteboardImage) => {\n      if (onDownload) {\n        onDownload(image);\n      } else {\n        const link = document.createElement('a');\n        link.href = image.imageUrl;\n        link.download = image.title || `whiteboard-${image.id}`;\n        link.target = '_blank';\n        link.rel = 'noopener noreferrer';\n        document.body.appendChild(link);\n        link.click();\n        document.body.removeChild(link);\n      }\n    },\n    [onDownload]\n  );\n\n  /**\n   * Handle image loading error\n   */\n  const handleImageError = useCallback((imageId: string) => {\n    setImageErrors((prev) => new Set(prev).add(imageId));\n  }, []);\n\n  const gridColsClass =\n    images?.length === 1\n      ? 'grid-cols-1'\n      : {\n          2: 'grid-cols-1 sm:grid-cols-2',\n          3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',\n          4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',\n        }[imagesPerRow];\n\n  // Let CSS handle sizing responsively\n\n  if (!images || images.length === 0) {\n    return (\n      <div\n        className={cn(\n          'flex items-center justify-center p-8 bg-background border border-border-50 rounded-xl',\n          className\n        )}\n        {...rest}\n      >\n        <p className=\"text-gray-400 text-sm\">Nenhuma imagem disponível</p>\n      </div>\n    );\n  }\n\n  return (\n    <div\n      className={cn(\n        'flex flex-col bg-background border border-border-50 p-4 gap-2 rounded-xl w-fit mx-auto',\n        className\n      )}\n      {...rest}\n    >\n      <div className={cn('grid gap-4', gridColsClass)}>\n        {images.map((image) => (\n          <div\n            key={image.id}\n            className=\"relative group overflow-hidden bg-gray-100 rounded-lg\"\n            style={{\n              width: `${IMAGE_WIDTH}px`,\n            }}\n          >\n            <div\n              className=\"relative\"\n              style={{\n                width: `${IMAGE_WIDTH}px`,\n                height: `${IMAGE_HEIGHT}px`,\n              }}\n            >\n              {imageErrors.has(image.id) ? (\n                <div className=\"absolute inset-0 flex items-center justify-center bg-gray-200\">\n                  <p className=\"text-gray-500 text-sm text-center px-2\">\n                    Imagem indisponível\n                  </p>\n                </div>\n              ) : (\n                <>\n                  <img\n                    src={image.imageUrl}\n                    alt={image.title || `Whiteboard ${image.id}`}\n                    className=\"absolute inset-0 w-full h-full object-cover\"\n                    loading=\"lazy\"\n                    onError={() => handleImageError(image.id)}\n                  />\n                  <div className=\"absolute inset-0 bg-gradient-to-t from-black/20 to-transparent\" />\n                </>\n              )}\n            </div>\n            {showDownload && (\n              <button\n                type=\"button\"\n                onClick={() => handleDownload(image)}\n                className=\"cursor-pointer absolute bottom-3 right-3 flex items-center justify-center bg-black/20 backdrop-blur-sm rounded hover:bg-black/30 transition-colors duration-200 group/button w-6 h-6\"\n                aria-label={`Download ${image.title || 'imagem'}`}\n              >\n                <ArrowsOut\n                  size={24}\n                  weight=\"regular\"\n                  className=\"text-white group-hover/button:scale-110 transition-transform duration-200\"\n                />\n              </button>\n            )}\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n};\n\nexport default Whiteboard;\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"],"mappings":"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","names":[]}