Início Boas práticas Tema Idiomas e RTL App e cabeçalho Layout Tipografia Abas Menu e âncora Dropdown e popover Command e toggles Botão Campos Seleção e valores Combobox e menções Datas e calendário Árvore e cascata Transfer Upload Filtros Tabela Planilha KPI e estatística Badge e tag Listas e detalhes Gráficos Imagem e carrossel Ícones Diagramas Alertas e estados Progresso e passos Diálogo e toast Drawer e notificação Utilitários Overview Work queue Report Report com htmx Form / request Tabela administrativa Página de detalhe Formulário em diálogo Gráfico com dados do servidor Buscar… llms.txt Tema Tema Restaurar Copiar prompt Copiar snippet Prompt: para as instruções de um agente. Snippet: direto no templates/base.html. v0.11.0 File upload with a drag-and-drop zone (click, drag or keyboard), validation and a file list. Notas fiscais com progressoO componente só seleciona e valida. Você envia com abCall e informa o andamento com setStatus(file, 'uploading' | 'done' | 'error'). Aqui o envio é simulado; arquivos com 'erro' no nome falham. Enviar notas Limpar <ab-stack gap="3"> <ab-upload id="nf-upload" name="notas" label="Notas fiscais" accept=".pdf,.xml" multiple max-size="5" max-files="10"></ab-upload> <ab-cluster> <ab-button id="nf-send" variant="primary" icon="upload-simple">Enviar notas</ab-button> <ab-button id="nf-clear" variant="ghost">Limpar</ab-button> </ab-cluster> </ab-stack> <script type="module"> // ab-upload is loaded on demand: wait for it before calling its methods at page load. await customElements.whenDefined("ab-upload"); const up = document.getElementById("nf-upload"); const pdf = (name, kb) => new File([new Uint8Array(kb * 1024)], name, { type: "application/pdf" }); const xml = (name) => new File(["<nfeProc/>"], name, { type: "text/xml" }); // Estado inicial para ilustrar: uma enviada, uma enviando, uma com erro e um arquivo grande recusado. const [a, b, c] = [pdf("NF-e 4512 - Fornecedor ABC.pdf", 312), xml("NF-e 4513.xml"), pdf("NF-e 4514 (erro).pdf", 840)]; up.files = [a, b, c]; up.setStatus(a, "done"); up.setStatus(b, "uploading", { progress: 62 }); up.setStatus(c, "error", { message: "Chave de acesso inválida" }); up.addFiles([pdf("Lote completo março.pdf", 6 * 1024)]); async function send(file) { // Na sua Page: await abCall("salvar_nota", { arquivo: file }); up.setStatus(file, "uploading", { progress: 0 }); for (let p = 15; p <= 100; p += 15 + Math.random() * 20) { await new Promise((r) => setTimeout(r, 250)); up.setStatus(file, "uploading", { progress: Math.min(p, 99) }); } if (/erro/i.test(file.name)) up.setStatus(file, "error", { message: "Chave de acesso inválida" }); else up.setStatus(file, "done"); } document.getElementById("nf-send").addEventListener("click", async (e) => { const btn = e.currentTarget; const pending = up.files.filter((f) => up.getStatus(f)?.status !== "done"); if (!pending.length) return abToast("Nada para enviar", { tone: "info" }); btn.loading = true; await Promise.all(pending.map(send)); btn.loading = false; const failed = up.files.filter((f) => up.getStatus(f)?.status === "error").length; abToast(failed ? `${failed} nota(s) com erro` : "Notas enviadas", { tone: failed ? "danger" : "ok" }); }); document.getElementById("nf-clear").addEventListener("click", () => up.clear()); </script> Comprovantes com miniaturalist-type picture mostra a imagem de cada recibo. PDFs ficam com o ícone do tipo. <ab-upload id="receipts" label="Comprovantes de despesa" accept="image/*,.pdf" multiple list-type="picture" max-size="10" help="Foto legível do cupom ou nota. Até 10 MB por arquivo."></ab-upload> <script type="module"> const svg = (bg, label) => `<svg xmlns="http://www.w3.org/2000/svg" width="120" height="160"><rect width="120" height="160" fill="${bg}"/><rect x="14" y="16" width="92" height="10" rx="3" fill="#ffffffcc"/><rect x="14" y="36" width="70" height="6" rx="3" fill="#ffffff99"/><rect x="14" y="48" width="80" height="6" rx="3" fill="#ffffff99"/><rect x="14" y="60" width="60" height="6" rx="3" fill="#ffffff99"/><text x="14" y="140" font-family="sans-serif" font-size="16" font-weight="700" fill="#fff">${label}</text></svg>`; const img = (name, bg, label) => new File([svg(bg, label)], name, { type: "image/svg+xml" }); const up = document.getElementById("receipts"); up.files = [ img("almoco-cliente.svg", "#2f855a", "R$ 186,40"), img("taxi-aeroporto.svg", "#3d79d8", "R$ 74,90"), new File([new Uint8Array(220 * 1024)], "hotel-recife.pdf", { type: "application/pdf" }), ]; </script> Em um formulárioab-upload participa do FormData: new FormData(form).getAll(name) devolve os File. Ou passe upload.files direto para abCall.
Cadastrar—
<form id="contrato-form"> <ab-stack gap="3"> <ab-input name="fornecedor" label="Fornecedor" value="Acme Ltda"></ab-input> <ab-upload name="contrato" label="Contrato assinado" accept=".pdf" required max-size="20" compact></ab-upload> <ab-cluster><ab-button type="submit" variant="primary">Cadastrar</ab-button><code id="contrato-out">—</code></ab-cluster> </ab-stack> </form> <script type="module"> const form = document.getElementById("contrato-form"); form.addEventListener("submit", (e) => { e.preventDefault(); if (!form.reportValidity()) return; const fd = new FormData(form); // await abCall("cadastrar_contrato", { fornecedor: fd.get("fornecedor"), contrato: fd.get("contrato") }); document.getElementById("contrato-out").textContent = `fornecedor=${fd.get("fornecedor")}, contrato=${fd.get("contrato").name}`; }); </script>
Desabilitado<ab-upload label="Anexos" disabled hint="Aguardando aprovação do pedido"></ab-upload> APIAtributos, propriedades, métodos, eventos e slots de cada tag. <ab-upload>File upload with a drag-and-drop zone (click, drag or keyboard), validation and a file list. Form-associated: inside a <form>, new FormData(form).getAll(name) returns the File objects. It does NOT upload by itself — send the files with abCall("fn", { files: upload.files }) or your own fetch, and report progress back with upload.setStatus(file, "uploading", { progress: 40 }), then "done" or "error" (with { message }). Validation (accept, max-size, max-files) rejects files with an inline error; accepted files are in files.