ModalContainer, ModalHeader e ModalFooter) oferecem uma solução completa e acessível para criação de diálogos e overlays. Com animações suaves, bloqueio de scroll e controle de foco, proporcionam uma experiência de usuário profissional e intuitiva.tsx
import { ModalContainer, ModalHeader, ModalFooter } from "@arkyn/components";
tsx
function BasicModalExample() {const [isOpen, setIsOpen] = useState(false);return (<><div className="noContent column"><Button onClick={() => setIsOpen(true)}>Abrir Modal Básico</Button></div><ModalContainer isVisible={isOpen} makeInvisible={() => setIsOpen(false)}><ModalHeader>Modal de exemplo</ModalHeader><p style={{ padding: "16px", width: "400px" }}>Conteúdo do modal vai aqui.</p><ModalFooter><Button onClick={() => setIsOpen(false)}>Fechar</Button></ModalFooter></ModalContainer></>);}
tsx
function CompleteModalExample() {const [isOpen, setIsOpen] = useState(false);return (<><Button onClick={() => setIsOpen(true)}>Abrir Modal</Button><ModalContainer isVisible={isOpen} makeInvisible={() => setIsOpen(false)}><div className="modal-content"><ModalHeader><h2>Título do Modal</h2><p>Descrição ou subtítulo opcional</p></ModalHeader><main style={{ padding: "24px" }}><p>Conteúdo principal do modal vai aqui.</p><p>Pode incluir formulários, textos, imagens, etc.</p></main><ModalFooter><Button variant="outline" onClick={() => setIsOpen(false)}>Cancelar</Button><Button onClick={() => setIsOpen(false)}>Confirmar</Button></ModalFooter></div></ModalContainer></>);}
tsx
function ConfirmationModalExample() {const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);const handleDelete = () => {// Lógica de exclusãoconsole.log("Item deletado");setIsDeleteModalOpen(false);};return (<><Button variant="danger" onClick={() => setIsDeleteModalOpen(true)}><Trash2 size={16} style={{ marginRight: "8px" }} />Deletar Item</Button><ModalContainerisVisible={isDeleteModalOpen}makeInvisible={() => setIsDeleteModalOpen(false)}><div className="modal-content"><ModalHeader><div style={{ display: "flex", alignItems: "center" }}><AlertTrianglesize={24}color="#ef4444"style={{ marginRight: "12px" }}/><div><h2>Confirmar Exclusão</h2><p>Esta ação não pode ser desfeita</p></div></div></ModalHeader><main style={{ padding: "24px" }}><p>Tem certeza de que deseja excluir este item? Todos os dadosrelacionados serão perdidos permanentemente.</p></main><ModalFooter alignment="right"><Buttonvariant="outline"onClick={() => setIsDeleteModalOpen(false)}>Cancelar</Button><Button variant="danger" onClick={handleDelete}><Trash2 size={16} style={{ marginRight: "8px" }} />Deletar</Button></ModalFooter></div></ModalContainer></>);}
tsx
function FormModalExample() {const [isFormModalOpen, setIsFormModalOpen] = useState(false);const [formData, setFormData] = useState({name: "",email: "",role: "",});const handleSave = () => {// Validar e salvar dadosconsole.log("Dados salvos:", formData);setIsFormModalOpen(false);};return (<><Button onClick={() => setIsFormModalOpen(true)}><Plus size={16} style={{ marginRight: "8px" }} />Novo Usuário</Button><ModalContainerisVisible={isFormModalOpen}makeInvisible={() => setIsFormModalOpen(false)}><div className="modal-content" style={{ width: "500px" }}><ModalHeader><div style={{ display: "flex", alignItems: "center" }}><User size={24} style={{ marginRight: "12px" }} /><div><h2>Adicionar Novo Usuário</h2><p>Preencha as informações do usuário</p></div></div></ModalHeader><main style={{ padding: "24px" }}><divstyle={{ display: "flex", flexDirection: "column", gap: "16px" }}><Inputlabel="Nome Completo"name="name"placeholder="Digite o nome completo"value={formData.name}onChange={(e) =>setFormData((prev) => ({...prev,name: e.target.value,}))}/><Inputlabel="Email"name="email"type="email"placeholder="usuario@email.com"value={formData.email}onChange={(e) =>setFormData((prev) => ({...prev,email: e.target.value,}))}/><Inputlabel="Função"name="role"placeholder="Ex: Desenvolvedor"value={formData.role}onChange={(e) =>setFormData((prev) => ({...prev,role: e.target.value,}))}/></div></main><ModalFooter alignment="right"><Button variant="outline" onClick={() => setIsFormModalOpen(false)}>Cancelar</Button><Button onClick={handleSave}><Save size={16} style={{ marginRight: "8px" }} />Salvar Usuário</Button></ModalFooter></div></ModalContainer></>);}
tsx
function InfoModalExample() {const [isInfoModalOpen, setIsInfoModalOpen] = useState(false);return (<><Button variant="info" onClick={() => setIsInfoModalOpen(true)}><Info size={16} style={{ marginRight: "8px" }} />Ver Informações</Button><ModalContainerisVisible={isInfoModalOpen}makeInvisible={() => setIsInfoModalOpen(false)}><div className="modal-content"><ModalHeader><div style={{ display: "flex", alignItems: "center" }}><Info size={24} color="#3b82f6" style={{ marginRight: "12px" }} /><div><h2>Informações do Sistema</h2><p>Detalhes sobre a versão e recursos</p></div></div></ModalHeader><main style={{ padding: "24px" }}><divstyle={{ display: "flex", flexDirection: "column", gap: "12px" }}><div><strong>Versão:</strong> 2.1.4</div><div><strong>Última atualização:</strong> 15 de Janeiro, 2024</div><div><strong>Recursos disponíveis:</strong><ul style={{ marginTop: "8px", paddingLeft: "20px" }}><li>Dashboard interativo</li><li>Relatórios avançados</li><li>Integração com APIs</li><li>Notificações em tempo real</li></ul></div></div></main><ModalFooter alignment="center"><Button onClick={() => setIsInfoModalOpen(false)}><CheckCircle size={16} style={{ marginRight: "8px" }} />Entendi</Button></ModalFooter></div></ModalContainer></>);}
ModalFooter oferece diferentes opções de alinhamento para os botões.tsx
// Alinhamento à esquerda<ModalFooter alignment="left"><Button variant="danger">Deletar</Button></ModalFooter>// Alinhamento centralizado<ModalFooter alignment="center"><Button>OK</Button></ModalFooter>// Alinhamento à direita (padrão)<ModalFooter alignment="right"><Button variant="outline">Cancelar</Button><Button>Confirmar</Button></ModalFooter>// Espaço entre botões<ModalFooter alignment="between"><Button variant="outline">Anterior</Button><Button>Próximo</Button></ModalFooter>// Espaço ao redor dos botões<ModalFooter alignment="around"><Button variant="outline">Cancelar</Button><Button variant="info">Salvar Rascunho</Button><Button>Publicar</Button></ModalFooter>
tsx
function MandatoryModalExample() {const [isMandatoryModalOpen, setIsMandatoryModalOpen] = useState(false);const [step, setStep] = useState(1);const handleComplete = () => {if (step < 3) {setStep(step + 1);} else {setIsMandatoryModalOpen(false);setStep(1);}};return (<><Button onClick={() => setIsMandatoryModalOpen(true)}>Iniciar Processo Obrigatório</Button><ModalContainerisVisible={isMandatoryModalOpen}makeInvisible={() => {}} // Não permite fechar clicando fora><div className="modal-content"><ModalHeader showCloseButton={false}><div><h2>Processo Obrigatório</h2><p>Etapa {step} de 3 - Complete para continuar</p></div></ModalHeader><main style={{ padding: "24px" }}>{step === 1 && (<div><h3>Etapa 1: Termos de Uso</h3><p>Leia e aceite os termos de uso.</p></div>)}{step === 2 && (<div><h3>Etapa 2: Configuração</h3><p>Configure suas preferências iniciais.</p></div>)}{step === 3 && (<div><h3>Etapa 3: Confirmação</h3><p>Confirme suas configurações.</p></div>)}</main><ModalFooter alignment="right"><Button onClick={handleComplete}>{step < 3 ? "Próximo" : "Finalizar"}</Button></ModalFooter></div></ModalContainer></>);}
tsx
function UploadModalExample() {const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);const [uploadProgress, setUploadProgress] = useState(0);const [isUploading, setIsUploading] = useState(false);const handleUpload = async () => {setIsUploading(true);// Simular progresso de uploadfor (let i = 0; i <= 100; i += 10) {await new Promise((resolve) => setTimeout(resolve, 200));setUploadProgress(i);}setIsUploading(false);setUploadProgress(0);setIsUploadModalOpen(false);};return (<><Button onClick={() => setIsUploadModalOpen(true)}><Upload size={16} style={{ marginRight: "8px" }} />Upload de Arquivo</Button><ModalContainerisVisible={isUploadModalOpen}makeInvisible={() => !isUploading && setIsUploadModalOpen(false)}><div className="modal-content"><ModalHeader showCloseButton={!isUploading}><div style={{ display: "flex", alignItems: "center" }}><Upload size={24} style={{ marginRight: "12px" }} /><div><h2>Upload de Arquivo</h2><p>Selecione e envie seus arquivos</p></div></div></ModalHeader><main style={{ padding: "24px" }}>{!isUploading ? (<div><divstyle={{border: "2px dashed #d1d5db",borderRadius: "8px",padding: "40px",textAlign: "center",cursor: "pointer",}}><Uploadsize={48}style={{ margin: "0 auto 16px", opacity: 0.5 }}/><p>Clique para selecionar arquivos ou arraste aqui</p><p style={{ fontSize: "14px", opacity: 0.7 }}>Formatos suportados: PDF, DOC, JPG, PNG</p></div></div>) : (<div><p>Enviando arquivo...</p><divstyle={{width: "100%",height: "8px",backgroundColor: "#f3f4f6",borderRadius: "4px",overflow: "hidden",marginTop: "12px",}}><divstyle={{width: `${uploadProgress}%`,height: "100%",backgroundColor: "#3b82f6",transition: "width 0.2s ease",}}/></div><p style={{ marginTop: "8px", fontSize: "14px" }}>{uploadProgress}% concluído</p></div>)}</main><ModalFooter alignment="right"><Buttonvariant="outline"onClick={() => setIsUploadModalOpen(false)}disabled={isUploading}>Cancelar</Button><Button onClick={handleUpload} disabled={isUploading}>{isUploading ? "Enviando..." : "Iniciar Upload"}</Button></ModalFooter></div></ModalContainer></>);}
tsx
function NestedModalExample() {const [isMainModalOpen, setIsMainModalOpen] = useState(false);const [isNestedModalOpen, setIsNestedModalOpen] = useState(false);return (<><Button onClick={() => setIsMainModalOpen(true)}>Configurações Avançadas</Button>{/* Modal Principal */}<ModalContainerisVisible={isMainModalOpen}makeInvisible={() => setIsMainModalOpen(false)}><div className="modal-content"><ModalHeader><div style={{ display: "flex", alignItems: "center" }}><Settings size={24} style={{ marginRight: "12px" }} /><div><h2>Configurações</h2><p>Gerencie suas preferências</p></div></div></ModalHeader><main style={{ padding: "24px" }}><p>Configurações gerais do sistema.</p><Buttonvariant="outline"onClick={() => setIsNestedModalOpen(true)}style={{ marginTop: "16px" }}>Configurações Avançadas</Button></main><ModalFooter><Button onClick={() => setIsMainModalOpen(false)}>Fechar</Button></ModalFooter></div></ModalContainer>{/* Modal Aninhado */}<ModalContainerisVisible={isNestedModalOpen}makeInvisible={() => setIsNestedModalOpen(false)}><div className="modal-content"><ModalHeader><h2>Configurações Avançadas</h2></ModalHeader><main style={{ padding: "24px" }}><p>Configurações técnicas e avançadas.</p><divstyle={{display: "flex",flexDirection: "column",gap: "12px",marginTop: "16px",}}><Inputlabel="API Endpoint"placeholder="https://api.exemplo.com"/><Input label="Timeout (ms)" placeholder="5000" type="number" /></div></main><ModalFooter><Buttonvariant="outline"onClick={() => setIsNestedModalOpen(false)}>Cancelar</Button><Button onClick={() => setIsNestedModalOpen(false)}>Salvar</Button></ModalFooter></div></ModalContainer></>);}
isVisible - boolean (obrigatório)
Controla o estado de visibilidade do modalmakeInvisible - () => void (obrigatório)
Função callback para fechar o modalchildren - ReactNode
Conteúdo a ser exibido dentro do modalclassName - string
Classes CSS adicionais para estilizaçãoshowCloseButton - boolean (padrão: true)
Se deve exibir o botão de fechar (X) no headerchildren - ReactNode
Conteúdo do header (título, descrição, etc.)className - string
Classes CSS adicionais para estilizaçãoalignment - "left" | "center" | "right" | "between" | "around" (padrão: "right")
Alinhamento do conteúdo dentro do footerchildren - ReactNode
Conteúdo do footer (botões, links, etc.)className - string
Classes CSS adicionais para estilizaçãoaside. ModalHeader
e ModalFooter suportam todas as propriedades HTML de header e footer
respectivamente.makeInvisibletsx
// Exemplo com tecla ESCfunction ModalWithEscapeKey() {const [isOpen, setIsOpen] = useState(false);useEffect(() => {const handleEscape = (event: KeyboardEvent) => {if (event.key === "Escape" && isOpen) {setIsOpen(false);}};if (isOpen) {document.addEventListener("keydown", handleEscape);}return () => {document.removeEventListener("keydown", handleEscape);};}, [isOpen]);return (<ModalContainer isVisible={isOpen} makeInvisible={() => setIsOpen(false)}>{/* Conteúdo do modal */}</ModalContainer>);}
role="dialog" e aria-modaltsx
<ModalContainerisVisible={isOpen}makeInvisible={() => setIsOpen(false)}role="dialog"aria-modal="true"aria-labelledby="modal-title"aria-describedby="modal-description"><div className="modal-content"><ModalHeader><h2 id="modal-title">Título do Modal</h2><p id="modal-description">Descrição do modal</p></ModalHeader><main>{/* Conteúdo */}</main><ModalFooter><Button onClick={() => setIsOpen(false)}>Fechar</Button></ModalFooter></div></ModalContainer>
tsx
// Deletar, arquivar, desativarconst useConfirmationModal = (action: () => void) => {const [isOpen, setIsOpen] = useState(false);const confirm = () => {action();setIsOpen(false);};return { isOpen, setIsOpen, confirm };};
tsx
// Criar usuário, editar perfil, configuraçõesfunction FormModal({ isOpen, onClose, initialData }) {return (<ModalContainer isVisible={isOpen} makeInvisible={onClose}><form onSubmit={handleSubmit}><ModalHeader><h2>{initialData ? "Editar" : "Criar"} Item</h2></ModalHeader><main>{/* Campos do formulário */}</main><ModalFooter><Button type="button" onClick={onClose}>Cancelar</Button><Button type="submit">Salvar</Button></ModalFooter></form></ModalContainer>);}
tsx
// Detalhes do produto, informações do usuáriofunction DetailsModal({ item, isOpen, onClose }) {return (<ModalContainer isVisible={isOpen} makeInvisible={onClose}><ModalHeader><h2>{item.title}</h2></ModalHeader><main><div className="details-content">{/* Informações detalhadas */}</div></main><ModalFooter><Button onClick={onClose}>Fechar</Button></ModalFooter></ModalContainer>);}
tsx
// Carregamento sob demanda do conteúdoconst LazyModalContent = lazy(() => import("./ModalContent"));function OptimizedModal({ isOpen, onClose }) {return (<ModalContainer isVisible={isOpen} makeInvisible={onClose}><Suspense fallback={<div>Carregando...</div>}><LazyModalContent /></Suspense></ModalContainer>);}
tsx
// Evitar re-renderizações desnecessáriasconst MemoizedModal = memo(function Modal({ isOpen, onClose, data }) {return (<ModalContainer isVisible={isOpen} makeInvisible={onClose}>{/* Conteúdo */}</ModalContainer>);});