{"version":3,"sources":["../src/components/RichEditor/components/ImageDialog.tsx"],"sourcesContent":["import { useState, useRef, ChangeEvent } from 'react';\nimport Button from '../../Button/Button';\nimport Input from '../../Input/Input';\nimport ImageUpload from '../../ImageUpload/ImageUpload';\nimport Modal from '../../Modal/Modal';\nimport Text from '../../Text/Text';\nimport { LinkIcon } from '@phosphor-icons/react/dist/csr/Link';\nimport { UploadSimpleIcon } from '@phosphor-icons/react/dist/csr/UploadSimple';\n\n/**\n * Matches the 5MB cap enforced by the backend pre-signed URL schema.\n * Keeping it here avoids the client accepting files the server will reject.\n */\nexport const MAX_IMAGE_SIZE = 5 * 1024 * 1024;\n\ntype InputMode = 'file' | 'url';\n\ninterface ImageDialogProps {\n  readonly open: boolean;\n  readonly onClose: () => void;\n  readonly onInsert: (src: string, alt: string) => void;\n  /**\n   * Optional callback to upload an image file and get back its public URL.\n   * If provided, the file upload tab is enabled; otherwise only URL input is\n   * available. The library stays agnostic of how the consumer uploads.\n   * @param file - The image file selected by the user\n   * @returns Promise resolving to the public URL of the uploaded image\n   */\n  readonly onUploadImage?: (file: File) => Promise<string>;\n}\n\nexport function ImageDialog({\n  open,\n  onClose,\n  onInsert,\n  onUploadImage,\n}: ImageDialogProps) {\n  const [inputMode, setInputMode] = useState<InputMode>(\n    onUploadImage ? 'file' : 'url'\n  );\n  const [file, setFile] = useState<File | null>(null);\n  const [url, setUrl] = useState('');\n  const [alt, setAlt] = useState('');\n  const [isUploading, setIsUploading] = useState(false);\n  const [error, setError] = useState('');\n  /** Bumped on close so a resolving upload can tell it has been superseded. */\n  const uploadTokenRef = useRef(0);\n\n  const resetState = () => {\n    setInputMode(onUploadImage ? 'file' : 'url');\n    setFile(null);\n    setUrl('');\n    setAlt('');\n    setIsUploading(false);\n    setError('');\n  };\n\n  const handleClose = () => {\n    // Invalidates any upload still in flight. The Modal closes on Escape and on\n    // its X button, so the user can dismiss the dialog mid-upload; without this\n    // the promise would resolve afterwards and insert an image they cancelled.\n    uploadTokenRef.current += 1;\n    resetState();\n    onClose();\n  };\n\n  const handleFileSelect = (selected: File) => {\n    setFile(selected);\n    setError('');\n  };\n\n  const handleSizeError = () => {\n    setFile(null);\n    setError('A imagem deve ter no máximo 5MB.');\n  };\n\n  const handleTypeError = () => {\n    setFile(null);\n    setError('Selecione um arquivo de imagem válido.');\n  };\n\n  const switchMode = (mode: InputMode) => {\n    setInputMode(mode);\n    setError('');\n  };\n\n  const handleInsert = async () => {\n    if (inputMode === 'url') {\n      if (!url.trim()) return;\n      onInsert(url.trim(), alt.trim());\n      resetState();\n      return;\n    }\n\n    if (!file || !onUploadImage) return;\n\n    setIsUploading(true);\n    setError('');\n    const token = uploadTokenRef.current;\n\n    try {\n      // Upload before inserting so the saved HTML never holds a blob: URL,\n      // which would break as soon as the page unloads.\n      const uploadedUrl = await onUploadImage(file);\n      if (token !== uploadTokenRef.current) return;\n      onInsert(uploadedUrl, alt.trim());\n      resetState();\n    } catch (err) {\n      if (token !== uploadTokenRef.current) return;\n      setError(err instanceof Error ? err.message : 'Erro ao enviar a imagem.');\n    } finally {\n      setIsUploading(false);\n    }\n  };\n\n  const canInsert = inputMode === 'url' ? !!url.trim() : !!file;\n\n  return (\n    <Modal\n      isOpen={open}\n      onClose={handleClose}\n      title=\"Inserir imagem\"\n      size=\"md\"\n      footer={\n        <>\n          <Button\n            variant=\"outline\"\n            onClick={handleClose}\n            disabled={isUploading}\n          >\n            Cancelar\n          </Button>\n          <Button\n            variant=\"solid\"\n            action=\"primary\"\n            onClick={handleInsert}\n            disabled={!canInsert || isUploading}\n          >\n            {isUploading ? 'Enviando...' : 'Inserir imagem'}\n          </Button>\n        </>\n      }\n    >\n      <div className=\"flex flex-col gap-4\">\n        {onUploadImage && (\n          <div className=\"flex gap-2\">\n            <Button\n              type=\"button\"\n              variant={inputMode === 'file' ? 'solid' : 'outline'}\n              action=\"primary\"\n              size=\"small\"\n              onClick={() => switchMode('file')}\n              className=\"flex items-center gap-1\"\n            >\n              <UploadSimpleIcon size={14} />\n              Enviar arquivo\n            </Button>\n            <Button\n              type=\"button\"\n              variant={inputMode === 'url' ? 'solid' : 'outline'}\n              action=\"primary\"\n              size=\"small\"\n              onClick={() => switchMode('url')}\n              className=\"flex items-center gap-1\"\n            >\n              <LinkIcon size={14} />\n              Usar URL\n            </Button>\n          </div>\n        )}\n\n        {inputMode === 'file' && onUploadImage ? (\n          <div>\n            <ImageUpload\n              selectedFile={file}\n              onFileSelect={handleFileSelect}\n              onRemoveFile={() => setFile(null)}\n              // Defaults to \"Inserir imagem\", which would collide with the\n              // dialog's confirm button — same name, different action.\n              buttonText=\"Selecionar arquivo\"\n              maxSize={MAX_IMAGE_SIZE}\n              onSizeError={handleSizeError}\n              onTypeError={handleTypeError}\n              disabled={isUploading}\n            />\n            <Text size=\"xs\" className=\"text-text-400 mt-1\">\n              Formatos aceitos: PNG, JPG, GIF, WEBP (máx. 5MB)\n            </Text>\n          </div>\n        ) : (\n          <div>\n            <Text weight=\"medium\" className=\"text-xs text-text-600 mb-2 block\">\n              URL da imagem\n            </Text>\n            <Input\n              type=\"text\"\n              value={url}\n              onChange={(e: ChangeEvent<HTMLInputElement>) =>\n                setUrl(e.target.value)\n              }\n              placeholder=\"https://exemplo.com/imagem.png\"\n            />\n          </div>\n        )}\n\n        <div>\n          <Text weight=\"medium\" className=\"text-xs text-text-600 mb-2 block\">\n            Texto alternativo (opcional)\n          </Text>\n          <Input\n            type=\"text\"\n            value={alt}\n            onChange={(e: ChangeEvent<HTMLInputElement>) =>\n              setAlt(e.target.value)\n            }\n            placeholder=\"Descreva a imagem para leitores de tela\"\n            disabled={isUploading}\n          />\n        </div>\n\n        {error && (\n          <Text size=\"xs\" className=\"text-error-600\">\n            {error}\n          </Text>\n        )}\n      </div>\n    </Modal>\n  );\n}\n"],"mappings":"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","names":[]}