{"version":3,"sources":["../src/components/Modal/Modal.tsx"],"sourcesContent":["import { ReactNode, useEffect, useId } from 'react';\nimport { XIcon } from '@phosphor-icons/react/dist/csr/X';\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 max-h-[calc(100dvh-2rem)] flex flex-col overflow-hidden';\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 XIcon */}\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                <XIcon 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 flex-1 min-h-0 overflow-y-auto\">\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              <XIcon size={18} />\n            </button>\n          )}\n        </div>\n\n        {/* Content */}\n        {children && (\n          <div\n            className={cn(\n              'px-6 pb-6 flex-1 min-h-0 overflow-y-auto',\n              contentClassName\n            )}\n          >\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"],"mappings":";;;;;;;;;;;;;AAAA,SAAoB,WAAW,aAAa;AAC5C,SAAS,aAAa;AA6NN,cAmCF,YAnCE;AAjNhB,IAAM,eAAe;AAAA,EACnB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AA8EA,IAAM,QAAQ,CAAC;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB;AAAA,EACA,kBAAkB;AAAA,EAClB,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,mBAAmB;AACrB,MAAkB;AAChB,QAAM,UAAU,MAAM;AAGtB,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,CAAC,cAAe;AAE/B,UAAM,eAAe,CAAC,UAAoC;AACxD,UAAI,MAAM,QAAQ,UAAU;AAC1B,gBAAQ;AAAA,MACV;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,YAAY;AACjD,WAAO,MAAM,SAAS,oBAAoB,WAAW,YAAY;AAAA,EACnE,GAAG,CAAC,QAAQ,eAAe,OAAO,CAAC;AAGnC,YAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AAGb,UAAM,iBACJ,OAAO,aAAa,SAAS,gBAAgB;AAG/C,UAAM,mBAAmB,SAAS,KAAK,MAAM;AAC7C,UAAM,uBAAuB,SAAS,KAAK,MAAM;AAGjD,aAAS,KAAK,MAAM,WAAW;AAG/B,QAAI,iBAAiB,GAAG;AACtB,eAAS,KAAK,MAAM,eAAe,GAAG,cAAc;AAGpD,YAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,cAAQ,KAAK;AACb,cAAQ,MAAM,UAAU;AAAA;AAAA;AAAA;AAAA,iBAIb,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAMzB,eAAS,KAAK,YAAY,OAAO;AAAA,IACnC;AAEA,WAAO,MAAM;AAEX,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,eAAe;AAGnC,YAAM,UAAU,SAAS,eAAe,yBAAyB;AACjE,UAAI,SAAS;AACX,gBAAQ,OAAO;AAAA,MACjB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,cAAc,aAAa,IAAI;AACrC,QAAM,cACJ;AAEF,QAAM,qBAAqB;AAC3B,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAGA,QAAM,eAAe,CAAC,SACpB,gBAAgB,KAAK,IAAI,IAAI,OAAO,WAAW,IAAI;AAGrD,QAAM,oBAAoB,MAAM;AAC9B,QAAI,YAAY;AACd,aAAO,KAAK,aAAa,UAAU,GAAG,UAAU,qBAAqB;AAAA,IACvE;AAAA,EACF;AAGA,MAAI,YAAY,YAAY;AAC1B,WACE,oBAAC,SAAI,WAAU,8HACb;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,QACX,mBAAiB;AAAA,QACjB,cAAW;AAAA,QACX,MAAI;AAAA,QAGJ;AAAA,8BAAC,SAAI,WAAU,6BACZ,WAAC,mBACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cACV,cAAW;AAAA,cAEX,8BAAC,SAAM,MAAM,IAAI;AAAA;AAAA,UACnB,GAEJ;AAAA,UAGA,qBAAC,SAAI,WAAU,6EAEZ;AAAA,qBACC,oBAAC,SAAI,WAAU,uBACb;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,KAAK,YAAY;AAAA,gBACjB,WAAU;AAAA;AAAA,YACZ,GACF;AAAA,YAIF;AAAA,cAAC;AAAA;AAAA,gBACC,IAAI;AAAA,gBACJ,WAAU;AAAA,gBAET;AAAA;AAAA,YACH;AAAA,YAGC,eACC,oBAAC,OAAE,WAAU,yEACV,uBACH;AAAA,YAID,cACC,qBAAC,SAAI,WAAU,UACX;AAAA,qBAAM;AACN,sBAAM,aAAa,aAAa,UAAU;AAC1C,sBAAM,OAAO,aAAa,UAAU;AACpC,oBAAI,CAAC,KAAM,QAAO;AAClB,sBAAM,KAAK,kBAAkB,UAAU;AACvC,oBAAI,CAAC,IAAI;AACP,yBACE;AAAA,oBAAC;AAAA;AAAA,sBACC,SAAQ;AAAA,sBACR,QAAO;AAAA,sBACP,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,SAAS;AAAA,sBAER,yBAAe;AAAA;AAAA,kBAClB;AAAA,gBAEJ;AACA,uBACE;AAAA,kBAAC;AAAA;AAAA,oBACC,KAAK,mBAAmB,EAAE;AAAA,oBAC1B,WAAU;AAAA,oBACV,iBAAe;AAAA,oBACf,OAAM;AAAA,oBACN,OAAM;AAAA;AAAA,gBACR;AAAA,cAEJ,GAAG;AAAA,cACF,CAAC,aAAa,aAAa,UAAU,CAAC,KACrC;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAQ;AAAA,kBACR,QAAO;AAAA,kBACP,MAAK;AAAA,kBACL,WAAU;AAAA,kBACV,SAAS;AAAA,kBAER,yBAAe;AAAA;AAAA,cAClB;AAAA,eAEJ;AAAA,aAEJ;AAAA;AAAA;AAAA,IACF,GACF;AAAA,EAEJ;AAGA,SACE,oBAAC,SAAI,WAAU,8HACb;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,MACX,mBAAiB;AAAA,MACjB,cAAW;AAAA,MACX,MAAI;AAAA,MAGJ;AAAA,6BAAC,SAAI,WAAU,+CACb;AAAA,8BAAC,QAAG,IAAI,SAAS,WAAU,uCACxB,iBACH;AAAA,UACC,CAAC,mBACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cACV,cAAW;AAAA,cAEX,8BAAC,SAAM,MAAM,IAAI;AAAA;AAAA,UACnB;AAAA,WAEJ;AAAA,QAGC,YACC;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA;AAAA,YACF;AAAA,YAEA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW;AAAA,kBACT;AAAA,kBACA,kBAAkB,SAAS,MAAM,KAC/B;AAAA,gBACJ;AAAA,gBAEC;AAAA;AAAA,YACH;AAAA;AAAA,QACF;AAAA,QAID,UACC,oBAAC,SAAI,WAAU,oCAAoC,kBAAO;AAAA;AAAA;AAAA,EAE9D,GACF;AAEJ;AAEA,IAAO,gBAAQ;","names":[]}