@topjoao/top-design-system

Design system e biblioteca de componentes Vue da TopSolutions
5,877
Install
npm install @topjoao/top-design-system
Latest Version:0.2.17
Vue:3
License:Proprietary
Last Updated:Oct 21, 2026
Links: npm
Maintainer: topjoao

TopSolutions Design System

Biblioteca de componentes Vue 3, estilos e tokens visuais compartilhados pela TopSolutions.

Índice

Componentes públicos

Componente Finalidade v-model
TopButton Botão com estilos e estados da marca. —
TopConfirmDialog Confirmação modal controlada pela aplicação. boolean
TopDatePicker Campo de data, múltiplas datas ou intervalo. Date/array de Date
TopFileUpload Escolha, validação e prévia de arquivos; não faz upload. File/File[]/null
TopInputText Campo textual com label, erro e acessibilidade. string
TopTextArea Campo de texto multilinha com label, erro e altura configurável. string
TopInputNumber Campo numérico localizado, com moeda, limites e incrementos. number | null
TopNavBar Navegação responsiva, breadcrumbs, favoritos e Drawer. searchValue e mobileOpen opcionais
TopSelect Autocomplete pesquisável com paginação virtual. opção/opções
TopTabs Abas com painéis nomeados. string | number
TopToast Renderizador de notificações do serviço Toast do PrimeVue. —

Instalação

npm install @topjoao/top-design-system

A aplicação consumidora deve possuir Vue 3, PrimeVue, PrimeIcons e @primeuix/themes em versões compatíveis com as peerDependencies do pacote.

Tema TopSolutions

A biblioteca exporta TopSolutionsPreset, o preset PrimeVue baseado na paleta oficial da TopSolutions. Configure-o uma vez na aplicação consumidora:

import PrimeVue from 'primevue/config'
import { TopSolutionsPreset } from '@topjoao/top-design-system'

app.use(PrimeVue, {
  theme: {
    preset: TopSolutionsPreset,
    options: {
      darkModeSelector: '.dark',
      cssLayer: {
        name: 'primevue',
        order: 'primevue',
      },
    },
  },
})

A camada primevue mantém os estilos estruturais do tema abaixo dos padrões do Design System e das classes utilitárias da aplicação. Com isso, propriedades como headerClass e contentClass conseguem sobrescrever os valores padrão sem !important.

Para ativar o modo escuro, adicione ou remova .dark uma única vez no elemento <html> da aplicação. Os componentes usam tokens semânticos de superfície, texto e borda fornecidos por style.css; não é necessário passar classes dark: em cada uso. A aplicação pode substituir esses tokens após importar o CSS da biblioteca.

Os tokens públicos de campos são --top-field-background, --top-field-background-readonly, --top-field-background-disabled, --top-field-text, --top-field-label, --top-field-placeholder, --top-field-border, --top-field-border-hover e --top-field-icon.

O preset é independente dos ajustes de layout próprios do TopLicita; ele contém apenas tokens semânticos compartilháveis, como cores primárias, neutras, sucesso, alerta e erro.

style.css contém somente os estilos dos componentes públicos do Design System. Regras que alcançam o PrimeVue são encapsuladas pelas classes-raiz desses componentes; componentes PrimeVue usados diretamente pela aplicação consumidora não são sobrescritos pela biblioteca.

Tailwind CSS 4

A biblioteca compila seus estilos com Tailwind CSS 4.3 e o plugin @tailwindcss/vite. O tema, as fontes de classes e a variante .dark ficam em src/styles/index.css; não há configuração Tailwind em JavaScript nem plugin Tailwind no PostCSS.

O CSS publicado continua pronto para uso: a aplicação consumidora não precisa instalar Tailwind nem incluir os arquivos da biblioteca na sua busca de classes. As utilidades ficam restritas a .top-solutions-component, sem Preflight global. Os tokens --top-* continuam personalizáveis por tema ou componente. As classes exclusivas das histórias são geradas apenas pelo Storybook.

O Tailwind 4 exige Safari 16.4+, Chrome 111+ ou Firefox 128+. Consulte o guia oficial de migração para os requisitos de navegadores e as diferenças em relação à versão 3.

Uso com Nuxt

Adicione o módulo uma única vez ao nuxt.config.ts:

export default defineNuxtConfig({
  modules: [
    // outros módulos...
    '@topjoao/top-design-system/nuxt',
  ],
})

Depois de alterar a configuração, reinicie o servidor de desenvolvimento ou regenere os arquivos do Nuxt:

npm run postinstall

O módulo registra os componentes, gera suas tipagens e inclui o CSS da biblioteca. Não é necessário importar o componente manualmente:

<template>
  <TopButton label="Salvar" icon="pi pi-save" />
</template>

Uso com Vue 3

Importação por componente

<script setup lang="ts">
import { TopButton } from '@topjoao/top-design-system'
import '@topjoao/top-design-system/style.css'
</script>

<template>
  <TopButton label="Salvar" icon="pi pi-save" />
</template>

Registro global

Na inicialização da aplicação:

import { createApp } from 'vue'
import { TopSolutionsDesignSystem } from '@topjoao/top-design-system/plugin'
import '@topjoao/top-design-system/style.css'
import App from './App.vue'

createApp(App).use(TopSolutionsDesignSystem).mount('#app')

Após o registro, os componentes podem ser utilizados sem importação manual. A entrada /plugin também fornece as declarações globais usadas pela IDE.

TopNavBar

Barra de navegação responsiva inspirada no AppSidebar do TopLicita. O componente fornece apenas layout e interação: a aplicação consumidora continua responsável por rotas, permissões, sessão, cliente/órgão, busca remota, favoritos, Aia, suporte e integrações. Nenhuma dessas ações é executada pela biblioteca; todas são comunicadas por eventos.

<script setup lang="ts">
import { ref } from 'vue'
import {
  TopNavBar,
  type TopNavAction,
  type TopNavItem,
  type TopNavSection,
} from '@topjoao/top-design-system'

const clienteAtual = ref('prefeitura-a')
const favorito = ref(false)

const secoes: TopNavSection[] = [
  {
    id: 'planejamento',
    label: 'Planejamento',
    icon: 'pi pi-book',
    children: [
      {
        id: 'programacao',
        label: 'Programação',
        children: [
          { id: 'calendario', label: 'Calendário', to: '/programacao/calendario' },
        ],
      },
    ],
  },
]

function navegar(item: TopNavItem) {
  if (item.to) router.push(item.to)
}

function executarIntegracao(action: TopNavAction) {
  // Abra a integração identificada por action.id.
}
</script>

<template>
  <TopNavBar
    :sections="secoes"
    active-item-id="calendario"
    searchable
    show-aia
    show-support
    show-favorite-toggle
    :favorite-active="favorito"
    :actions="[
      { id: 'integracoes', label: 'Integrações', icon: 'pi pi-th-large' },
    ]"
    :user="{ name: 'João Silva', subtitle: 'Administrador', initials: 'JS' }"
    :user-menu-items="[
      { id: 'perfil', label: 'Meu perfil', icon: 'pi pi-user' },
      { id: 'sair', label: 'Sair', icon: 'pi pi-sign-out' },
    ]"
    @navigate="navegar"
    @search="consultarMenusPermitidos"
    @toggle-aia="alternarAia"
    @open-support="abrirCentralSuporte"
    @toggle-favorite="favorito = !favorito"
    @action="executarIntegracao"
    @user-action="executarAcaoDaSessao"
  >
    <template #brand="{ compact }">
      <img src="/logo.svg" alt="Minha organização">
      <span v-if="!compact">Sistema de Contratações</span>
    </template>

    <template #context="{ compact }">
      <select v-model="clienteAtual" aria-label="Cliente atual">
        <option value="prefeitura-a">Prefeitura A</option>
        <option value="prefeitura-b">Prefeitura B</option>
      </select>
      <span v-if="!compact">Poder Executivo</span>
    </template>
  </TopNavBar>
</template>

Menu e breadcrumbs

sections aceita uma árvore de profundidade arbitrária. Cada nó usa TopNavItem (id, label, to?, icon?, disabled?, children?, data?). Seções sem filhos são removidas e um children: [] nunca cria um painel vazio. No desktop, a primeira coluna do painel mestre–detalhe tem 16rem; os itens de cada nível são repartidos em colunas de 18rem, no máximo dez por coluna. Subníveis aparecem sempre à direita do nível de origem.

Breadcrumbs estão habilitados por padrão e começam por Início / Navegação. Há duas formas de fornecer a trilha:

  • informe breadcrumbs com itens TopNavBreadcrumb; sectionId liga o item a uma seção e menuId liga a qualquer nó com filhos;
  • omita breadcrumbs e informe activeItemId; a trilha é derivada da árvore.

Por exemplo, calendario dentro de Programação em Planejamento gera Início / Navegação / Planejamento / Programação / Calendário. A rota ativa não recebe fundo permanente nos menus desktop; somente hover e o ramo que está sendo explorado recebem destaque.

Props

Prop Tipo / padrão Finalidade
sections TopNavSection[] / [] Árvore de navegação já filtrada pela aplicação.
breadcrumbs TopNavBreadcrumb[] / [] Trilha explícita; vazia permite derivação por activeItemId.
homeItem TopNavItem / Início Item inicial emitido ao clicar em Início.
navigationLabel string / Navegação Rótulo do menu mestre.
activeItemId string Nó atual, usado no breadcrumb e no Drawer.
searchable boolean / false Habilita busca desktop e busca própria do Drawer.
searchValue string Valor opcionalmente controlado com v-model:search-value.
searchResults TopNavItem[] Resultados controlados; sem a prop, a árvore é filtrada localmente.
searchPlaceholder string Placeholder das duas buscas.
showAia, showSupport boolean / false Exibem as ações opcionais.
aiaActive boolean / false Estado visual do botão Aia.
aiaLabel, supportLabel string Textos acessíveis e rótulos do Drawer.
actions TopNavAction[] / [] Ações genéricas, como integrações, emitidas por action.
client TopNavClient Organização, cliente ou escopo ativo exibido no cabeçalho e como contexto do usuário no rodapé do Drawer; não cria ação nem seletor.
user TopNavUser Dados exclusivamente visuais do usuário.
userMenuItems TopNavUserMenuItem[] Opções emitidas por user-action; suporta separadores.
favoriteItems TopNavItem[] / [] Favoritos fornecidos pelo pai para dropdown e Drawer.
showFavoriteToggle boolean / false Exibe a estrela da página atual.
favoriteActive boolean / false Estado visual da estrela atual.
mobileOpen boolean Controle opcional com v-model:mobile-open.
appearance TopNavBarAppearance Tokens visuais locais descritos abaixo.
spacing TopNavBarSpacing Espaçamento do conteúdo no cabeçalho e breadcrumbs; preserva os padrões atuais quando omitido.

Eventos

Evento Payload Quando ocorre
navigate TopNavItem Início, menu, resultado ou favorito é selecionado.
search string A consulta muda no desktop ou no Drawer.
update:searchValue string Atualização de v-model:search-value.
update:mobileOpen boolean Atualização de v-model:mobile-open.
toggle-aia — A ação Aia é acionada.
open-support — A ação de suporte é acionada.
toggle-favorite — A estrela da página atual é acionada.
action TopNavAction Uma ação genérica/integração é acionada.
user-action TopNavUserMenuItem Uma opção de usuário é selecionada.
user-click TopNavUser | undefined A área de usuário sem menu é acionada.

Ctrl+K e Cmd+K abrem/focam a busca. Em telas menores que 1024px, o atalho abre primeiro o Drawer e foca a busca móvel. Escape fecha os painéis.

Slots

Slot Uso
brand Marca; recebe { compact }.
context, scope ou client Contexto operacional; aliases com prioridade nessa ordem, recebem { client, compact }. O slot client substitui a apresentação padrão da prop client.
context-compact Variante explícita usada no último estágio de overflow.
actions Conteúdo adicional do cabeçalho; recebe { compact, close }.
aia-icon, support-icon Ícones customizados das ações quadradas.
user Conteúdo do gatilho de usuário; recebe { user, compact }.
user-menu Painel de usuário; recebe { items, select }.
search-results Resultados customizados; recebe { items, select }.
breadcrumb-actions Ações adicionais no fim da segunda faixa.
drawer-header Cabeçalho inteiro; recebe { close }.
drawer-brand Marca do Drawer; por padrão reutiliza brand.
drawer-search Busca inteira; recebe { query, update, clear }.
drawer-actions Ações; recebe { actions, select, close }.
drawer-before-menu, drawer-after-menu Conteúdo antes/depois do trilho rolável.
drawer-menu Substitui a árvore; recebe { sections, select, close }.
drawer-user Rodapé de usuário; recebe { user, client, open, toggle }.
drawer-footer Conteúdo final adicional; recebe { close }.

Faixa de favoritos

No desktop, favoriteItems aparece primeiro em uma faixa horizontal abaixo dos breadcrumbs. A biblioteca mede uma cópia invisível da faixa com ResizeObserver: se a largura real não couber, ela é substituída pelo dropdown Favoritos. O mesmo dropdown é usado depois que a página passa de 120px de scroll e a faixa retorna apenas ao chegar a 24px ou menos. Os valores evitam oscilações perto do topo e reproduzem o comportamento do AppSidebar.

Aparência e responsividade

Use spacing para criar uma zona segura horizontal sem limitar o fundo das duas faixas. Os valores são aplicados no elemento raiz como --top-nav-header-padding e --top-nav-breadcrumb-padding; o cabeçalho e o breadcrumb continuam ocupando toda a largura da viewport. Em telas móveis, esses valores também são usados, salvo se a aplicação já definir as variáveis específicas --top-nav-header-padding-mobile ou --top-nav-breadcrumb-padding-mobile.

<TopNavBar
  :spacing="{
    headerPadding: '0.625rem max(2rem, calc((100vw - 80rem) / 2 + 2rem))',
    breadcrumbPadding: '0.25rem max(2rem, calc((100vw - 80rem) / 2 + 2rem))',
  }"
/>

appearance é propositalmente pequeno e semântico. Use colors para navigation, text, mutedText, accent, border, menuText, mobileActiveText e favoritesText; surfaces para breadcrumb, drawer, drawerFooter, menu, menuHover, menuExplored, mobileActive, favorites e currentPage; borders para menu (a borda externa de 6px), menuOutline, mobileDivider, favorites e currentPage; shape para radius, drawerRadius, shadow e favoritesShadow; e focus para onDark e onLight.

const appearance = {
  colors: { navigation: '#09090b', text: '#f4f4f5', border: 'rgb(255 255 255 / 14%)' },
  surfaces: { drawer: '#09090b', menu: '#18181b', menuHover: '#27272a' },
  shape: { radius: '8px', shadow: '0 12px 30px rgb(0 0 0 / 25%)' },
}

A biblioteca não fixa tipografia inline. Por padrão, família, tamanho, peso e altura de linha são herdados da aplicação consumidora. Além disso, appearance só cria variáveis inline para propriedades que foram realmente informadas; os valores fiéis ao AppSidebar existem apenas como fallbacks no CSS. Assim, um tema global pode controlar o componente sem precisar usar !important:

:root {
  --top-nav-font-family: var(--app-font-family);
  --top-nav-font-size: var(--app-font-size);
  --top-nav-strong-font-weight: 600;
  --top-nav-action-size: 2.5rem;
  --top-nav-header-padding: 0.625rem 1.5rem;
  --top-nav-breadcrumb-padding: 0.25rem 1.5rem;
  --top-nav-menu-item-padding: 0.5rem 0.75rem;
  --top-nav-section-width: 16rem;
  --top-nav-column-width: 18rem;
  --top-nav-mobile-active-bg: #fff;
  --top-nav-mobile-active-fg: #025a84;
  --top-nav-mobile-divider: rgb(255 255 255 / 20%);
  --top-nav-mobile-item-gap: 0.25rem;
  --top-nav-mobile-submenu-padding: 0.5rem 0 0.375rem 0.35rem;
  --top-nav-favorites-bg: #f8fafc;
  --top-nav-favorites-fg: #0f172a;
  --top-nav-favorites-border: #e2e8f0;
  --top-nav-favorites-shadow: 0 1px 2px rgb(15 23 42 / 6%);
  --top-nav-focus-ring: #dbeafe;
  --top-nav-focus-ring-light: #1d4ed8;
  --top-nav-current-bg: rgb(255 255 255 / 10%);
  --top-nav-current-border: rgb(255 255 255 / 15%);
  --top-nav-menu-scrollbar-thumb: #94a3b8;
}

As variáveis globais também alcançam o Drawer teleportado. Use-as para ajustes estruturais — tipografia, larguras, alturas, padding e espaçamentos do menu — em vez de props no componente. Um valor passado por appearance tem precedência local e não altera os demais tokens do tema.

O breakpoint móvel é 1024px. No desktop, um ResizeObserver reaplica a mesma sequência progressiva do AppSidebar: ações colapsam abaixo de 1440px, busca vira ícone abaixo de 1320px, nome do usuário some abaixo de 1180px e, se o conteúdo ainda transbordar, o contexto recebe compact: true. O último estágio também limita o contêiner do contexto a 3.25rem; use context-compact quando quiser controlar exatamente o que permanece visível.

Abaixo de 1024px, a navegação desktop desaparece e o Drawer do PrimeVue assume. Ele mantém cabeçalho, busca, ações, trilho translúcido rolável, árvore recursiva, favoritos e usuário. No rodapé, a foto e o nome do usuário formam a primeira linha; o cliente atual aparece abaixo e o subtítulo do cliente recebe uma etiqueta quando houver. A transição menu-expand existe somente dentro do Drawer; os dropdowns desktop abrem sem animação e suas áreas de hover incluem o espaço entre gatilho e painel. Nessa largura, os breadcrumbs deixam de rolar horizontalmente: eles quebram em linhas e o bloco da página atual ocupa sua própria linha para permanecer legível.

TopButton

Exemplo

<template>
  <TopButton label="Salvar" icon="pi pi-save" @click="salvar" />
  <TopButton label="Cancelar" secondary />
  <TopButton label="Excluir" severity="danger" />

  <TopButton label="Consultar" outlined>
    <template #icon>
      <i class="pi pi-search" />
    </template>
  </TopButton>
</template>
Prop Tipo Padrão
tooltip string ''
tooltipClass string 'text-xs'
label string ''
icon string ''
loading boolean false
class string ''
outlined boolean false
severity 'primary' | 'secondary' | 'success' | 'warn' | 'danger' 'primary'
secondary boolean false
disabled boolean false
unstyled boolean false
type 'button' | 'submit' | 'reset' 'button'

O componente emite click sem payload. O slot nomeado icon não recebe parâmetros e substitui a prop icon — útil para SVGs, HugeiconsIcon ou um ícone com estado próprio. secondary permanece como atalho compatível para severity="secondary" e tem precedência sobre severity.

O botão clicável interno expõe a classe estável top-button__control, que pode ser usada para customizar globalmente qualquer característica dos TopButton, como tipografia, cores, espaçamento, dimensões e bordas. Defina a regra no CSS global da aplicação, carregado depois do CSS do Design System:

.top-button__control {
  font-size: 0.875rem;
  font-weight: 600;
  min-height: 44px;
  padding: 0.75rem 1rem;
  border-radius: 0.5rem;
}

Em um projeto com Tailwind, a mesma regra pode usar @apply:

@layer components {
  .top-button__control {
    @apply min-h-11 rounded-lg px-4 py-3 text-sm font-semibold;
  }
}

A classe top-button__control atua no botão clicável, enquanto top-button atua no elemento externo que o envolve. A prop class continua disponível para customizar somente uma ocorrência. Algumas regras internas que usam !important podem exigir maior especificidade ou !important para serem sobrescritas.

TopConfirmDialog

Diálogo de confirmação baseado no Dialog do PrimeVue e nos botões do Design System. A visibilidade é controlada por v-model; a aplicação consumidora decide o que executar e quando encerrar após a confirmação.

<script setup lang="ts">
import { ref } from 'vue'
import { TopConfirmDialog } from '@topjoao/top-design-system'

const showConfirm = ref(false)

function excluirRegistro() {
  // Execute a ação e feche o diálogo quando apropriado.
  showConfirm.value = false
}
</script>

<template>
  <TopConfirmDialog
    v-model="showConfirm"
    title="Confirmar exclusão"
    message="Deseja realmente excluir este registro?"
    confirm-text="Excluir"
    cancel-text="Cancelar"
    @confirm="excluirRegistro"
  />
</template>
Prop Tipo Padrão
modelValue boolean false
title string 'Confirmar'
message string ''
confirmText / cancelText string 'Confirmar' / 'Cancelar'
loading boolean false
loadingText string 'Processando...'
severity 'primary' | 'danger' 'danger'
icon string 'pi pi-exclamation-triangle'

Eventos: update:modelValue, confirm, cancel e close. O evento confirm não fecha automaticamente o diálogo, permitindo que a aplicação aguarde uma operação assíncrona. Os slots message e default permitem substituir a mensagem textual.

TopDatePicker

Seletor de datas baseado no DatePicker do PrimeVue. O valor permanece como Date (ou arrays de Date nos modos multiple e range); dateFormat altera somente a apresentação no campo e não converte o v-model para texto. No modo padrão (single com dd/mm/yy), a digitação recebe automaticamente a máscara brasileira dd/mm/aaaa. Use showTime para habilitar a seleção de horário junto à data; nesse modo, a máscara de data é desativada para permitir a edição do horário no campo.

<script setup lang="ts">
import { ref } from 'vue'
import { TopDatePicker } from '@topjoao/top-design-system'

const dataNascimento = ref<Date | null>(null)
</script>

<template>
  <TopDatePicker
    v-model="dataNascimento"
    label="Data de nascimento"
    placeholder="Selecione a data"
    :max-date="new Date()"
    required
    error="Informe uma data válida."
    @date-select="validarData"
  />
</template>
Prop Tipo Padrão
modelValue Date | Date[] | (Date | null)[] | null null
label string ''
placeholder string 'dd/mm/aaaa'
required boolean false
error string ''
invalid boolean false
disabled boolean false
readonly boolean false
selectionMode 'single' | 'multiple' | 'range' 'single'
showTime boolean false
dateFormat string 'dd/mm/yy'
minDate / maxDate Date undefined
showIcon boolean true
iconDisplay 'button' | 'input' 'input'
manualInput boolean true
showButtonBar boolean false
appendTo 'body' | 'self' | HTMLElement 'body'

Eventos: update:modelValue, input, change, date-select, show, hide, today-click, clear-click, month-change, year-change, focus, blur e keydown. Os slots do DatePicker do PrimeVue são repassados pelo wrapper, incluindo date, header, footer, buttonbar, inputicon, dropdownicon, previcon e nexticon.

O slot date recebe as props de dia disponibilizadas pelo DatePicker; os slots de ícone recebem as props correspondentes do PrimeVue. Os slots header, footer e buttonbar permitem substituir essas regiões. Todos são apenas repassados: o wrapper preserva as props e o comportamento do componente-base.

required adiciona o atributo nativo e o asterisco visual; a validação continua sob responsabilidade da aplicação. error ativa o estado inválido, associa a mensagem ao input com atributos ARIA e a exibe abaixo do campo. readonly impede edição e seleção sem desabilitar o controle, enquanto disabled remove a interação. No modo escuro, o campo usa os tokens públicos --top-field-*.

TopFileUpload

Seletor de arquivos baseado no FileUpload do PrimeVue. O componente valida e apresenta os arquivos, mas não os envia: a aplicação consumidora controla o upload por v-model e pelos eventos.

<script setup lang="ts">
import { ref } from 'vue'
import { TopFileUpload } from '@topjoao/top-design-system'

const anexos = ref<File[]>([])

function enviarArquivos(files: File[]) {
  // Envie os arquivos usando o serviço da aplicação.
}
</script>

<template>
  <TopFileUpload
    v-model="anexos"
    label="Anexos"
    accept=".pdf,image/*"
    multiple
    :max-file-size="5 * 1024 * 1024"
    :max-files="5"
    required
    @select="enviarArquivos"
  />
</template>
Prop Tipo Padrão
modelValue File | File[] | null null
label string ''
placeholder string 'Arraste e solte o arquivo aqui'
required boolean false
disabled boolean false
accept string ''
multiple boolean false
maxFileSize number | null (bytes) null
maxFiles number | null null
error string ''
selectLabel string 'Selecionar arquivo'
removeLabel string 'Remover'
loading boolean false

Eventos: update:modelValue, select, change, remove, clear e error. Erros de tipo, tamanho e quantidade possuem code, message e o file relacionado. Os slots empty e preview permitem customizar a área vazia e a pré-visualização. Os métodos choose() e clear() ficam disponíveis pela ref do componente.

Slot Parâmetros recebidos Uso
empty { choose, disabled } Substitui a área vazia. Chame choose() para abrir o seletor nativo.
preview { file, index, url } Substitui a miniatura. url é uma object URL apenas para imagens; nos demais casos, é ''.
<TopFileUpload ref="upload" v-model="anexos" multiple>
  <template #empty="{ choose, disabled }">
    <button type="button" :disabled="disabled" @click="choose()">Anexar documentos</button>
  </template>
  <template #preview="{ file, url }">
    <img v-if="url" :src="url" :alt="file.name">
    <span v-else>{{ file.name }}</span>
  </template>
</TopFileUpload>

accept aceita extensões (.pdf), MIME types (application/pdf) e curingas MIME (image/*), separados por vírgula. maxFileSize é contado em bytes. Em modo simples, a última seleção válida substitui a anterior; com multiple, arquivos válidos são acumulados sem duplicar nome, tipo e tamanho.

TopInputNumber

Campo numérico baseado no InputNumber do PrimeVue, com a mesma estrutura de label, erro e acessibilidade dos demais campos. Por padrão usa pt-BR. No modo decimal, o usuário informa explicitamente o separador decimal; no modo currency, a máscara de centavos vem habilitada e transforma 12345 em R$ 123,45. O v-model permanece sempre numérico (number) ou null quando vazio.

<script setup lang="ts">
import { ref } from 'vue'
import { TopInputNumber } from '@topjoao/top-design-system'

const quantidade = ref<number | null>(null)
const valorUnitario = ref<number | null>(null)
</script>

<template>
  <TopInputNumber
    v-model="quantidade"
    label="Quantidade"
    :min="1"
    :min-fraction-digits="2"
    :max-fraction-digits="4"
    required
  />

  <TopInputNumber
    v-model="valorUnitario"
    label="Valor unitário"
    mode="currency"
    currency="BRL"
    :min-fraction-digits="2"
    :max-fraction-digits="2"
  />
</template>
Prop Tipo Padrão Finalidade
modelValue number | null null Valor numérico controlado.
label / placeholder string '' / '' Rótulo e texto auxiliar do campo.
required boolean false Asterisco visual e atributo nativo obrigatório.
error string '' Mensagem e estado inválido.
invalid boolean false Força estado inválido sem mostrar mensagem.
disabled / readonly boolean false / false Remove interação / preserva leitura sem desabilitar.
locale string 'pt-BR' Locale para separadores e moeda.
mode 'decimal' | 'currency' 'decimal' Formatação decimal ou monetária.
currency / currencyDisplay string / 'symbol' | 'code' | 'name' undefined / 'symbol' Código ISO 4217 e modo de exibição da moeda.
currencyInputMode 'decimal' | 'cents' 'cents' Controla a entrada monetária; decimal desliga a máscara mesmo quando cents é true. Não afeta mode="decimal".
cents boolean true Habilita a máscara automática somente em mode="currency"; false usa o InputNumber padrão.
centsFractionDigits number undefined (efetivo: 2) Primeira opção para definir as casas da máscara monetária.
currencyFractionDigits number undefined Fallback de casas da máscara, usado depois de centsFractionDigits.
useGrouping / format boolean true / true Separadores de milhar e formatação do valor.
minFractionDigits / maxFractionDigits number undefined Precisão do PrimeVue. Sem maxFractionDigits, o modo decimal aceita até 20 casas; a máscara monetária usa esse valor como último fallback antes de 2.
min / max / step number undefined / undefined / 1 Limites e passo dos botões/teclado.
showButtons / buttonLayout boolean / 'stacked' | 'horizontal' | 'vertical' false / 'stacked' Exibe e organiza os controles de incremento.
allowEmpty / showClear / highlightOnFocus boolean true / false / false Permite limpar, mostra ícone de limpeza e seleciona valor ao focar.

Eventos: update:modelValue (number | null), input, focus e blur. No fluxo padrão do InputNumber, o componente também repassa value-change. Atributos adicionais, como name, autocomplete, aria-* e data-*, são repassados ao controle interno.

Quando a máscara de centavos não está ativa, os slots do InputNumber também são repassados: incrementbutton e decrementbutton recebem { listeners }; incrementicon e decrementicon substituem os ícones; clearicon recebe { clearCallback }.

Para reproduzir a digitação monetária do TopLicita, a máscara de centavos já vem ativa em mode="currency". O usuário digita somente dígitos: 12345 resulta em R$ 123,45 e o v-model recebe 123.45. Para exigir a vírgula decimal digitada pelo usuário, passe :cents="false" ou currency-input-mode="decimal". Como a máscara controla o texto enquanto o usuário digita, nesse modo os botões incrementais, o ícone de limpeza e seus slots do InputNumber não são renderizados.

<TopInputNumber
  v-model="valorUnitario"
  label="Valor unitário"
  mode="currency"
  currency="BRL"
/>

TopInputText

Campo textual baseado no InputText do PrimeVue. O v-model é sempre string, inclusive para códigos, documentos e identificadores compostos somente por dígitos; por exemplo, "001234" preserva os zeros à esquerda.

<script setup lang="ts">
import { ref } from 'vue'
import { TopInputText } from '@topjoao/top-design-system'

const codigo = ref('001234')
</script>

<template>
  <TopInputText
    id="codigo"
    v-model="codigo"
    label="Código"
    placeholder="Digite o código"
    required
    maxlength="10"
    autocomplete="off"
  />
</template>
Prop Tipo Padrão
modelValue string ''
type string 'text'
label string ''
placeholder string ''
required boolean false
error string ''
disabled boolean false
readonly boolean false

Atributos e eventos nativos adicionais, como name, maxlength, autocomplete, inputmode, pattern, aria-*, data-*, focus e blur, são repassados ao elemento input interno. Use type="email" ou type="password" para os respectivos campos.

TopTextArea

Campo multilinha baseado no Textarea do PrimeVue. O v-model é uma string e preserva quebras de linha. A altura inicial é definida por rows; ative autoResize para acompanhar o conteúdo digitado. Por padrão, o usuário pode redimensionar o campo apenas na vertical; com autoResize, a altura é controlada automaticamente pelo componente.

<script setup lang="ts">
import { ref } from 'vue'
import { TopTextArea } from '@topjoao/top-design-system'

const descricao = ref('')
</script>

<template>
  <TopTextArea
    id="descricao"
    v-model="descricao"
    label="Descrição"
    placeholder="Descreva o item"
    :rows="5"
    :max-length="500"
    show-counter
    required
  />
</template>
Prop Tipo Padrão
modelValue string ''
label string ''
placeholder string ''
required boolean false
error string ''
disabled boolean false
readonly boolean false
rows number 4
autoResize boolean false
maxLength number undefined (sem limite)
showCounter boolean false

maxLength define o limite nativo de caracteres para digitação e colagem. showCounter exibe a contagem abaixo do campo, alinhada à direita, no formato 12 / 500 caracteres. Sem limite configurado, mostra apenas 12 caracteres. O contador acompanha o v-model; ocultá-lo não remove o limite. Valores atribuídos diretamente ao v-model não são truncados pelo limite nativo do navegador.

Atributos e eventos nativos adicionais, como name, maxlength, aria-*, data-*, focus e blur, são repassados ao elemento textarea interno. error ativa o estado inválido e associa a mensagem ao campo por ARIA.

TopSelect

Seletor pesquisável baseado no AutoComplete do PrimeVue. Ele é genérico: a aplicação fornece os itens, executa a busca e decide qualquer apresentação de domínio por slots.

<script setup lang="ts">
import { ref } from 'vue'
import { TopSelect } from '@topjoao/top-design-system'

const selectedCustomer = ref(null)
const customers = ref([])

function searchCustomers({ query }: { query: string }) {
  // Atualize customers com o resultado da sua fonte de dados.
}
</script>

<template>
  <TopSelect
    v-model="selectedCustomer"
    :options="customers"
    option-label="name"
    option-key="id"
    option-prefix="code"
    show-option-prefix
    :loading="false"
    @search="searchCustomers"
  >
    <template #icon="{ loading }">
      <i :class="loading ? 'pi pi-spin pi-spinner' : 'pi pi-users'" />
    </template>
    <template #footer>
      <button type="button">Criar cliente</button>
    </template>
  </TopSelect>
</template>
Prop Tipo Padrão
modelValue SelectOption | SelectOption[] | null null
options array []
optionLabel string | function 'label'
optionKey string 'id'
optionPrefix string ''
showOptionPrefix boolean false
showSelectedPrefix boolean false
loading / disabled / invalid boolean false
placeholder string 'Pesquisar...'
minQueryLength number 1
multiple / forceSelection boolean false / true
panelWidth / scrollHeight string null / '250px'
emptyMessage / loadingMessage string 'Nenhum resultado encontrado.' / 'Carregando...'
closeOnSelect boolean false

Eventos: update:modelValue, search, loadMore, clear, select e change.

Slots: icon, option, selected-item, chip, empty, option-group e footer. O slot icon recebe loading; sem ele, o componente mostra uma lupa ou um indicador de carregamento. Quando um slot não é informado, o componente usa sua apresentação padrão. O texto das opções é limitado visualmente pela largura disponível do painel, sem corte por quantidade fixa de caracteres; o tooltip padrão exibe o valor completo quando showTooltip está ativo.

Slot Parâmetros recebidos Uso
icon { loading } Ícone à esquerda do campo.
option Props nativas, mais { option, label, prefix, query } Linha de uma opção.
selected-item Props nativas, incluindo item Valor único escolhido.
chip Props nativas, incluindo value e removeCallback Tag no modo múltiplo.
empty — Conteúdo quando não há opções; também substitui o loading padrão.
option-group Props nativas, incluindo option Cabeçalho de um grupo.
footer Props nativas do AutoComplete Rodapé do painel.

search recebe { originalEvent, query } nas consultas digitadas e quando o botão abre o dropdown sem opções carregadas; se já houver opções, a abertura pelo botão reutiliza essa lista. Após limpar uma pesquisa, a próxima abertura com o campo vazio emite search com query: '' para recuperar a lista inicial, mesmo que ainda existam opções filtradas. Selecionar um item não dispara essa recuperação. Atualize options com os resultados e reinicie a paginação ao mudar a consulta. Se a aplicação já recarrega a lista na limpeza, a abertura pode solicitar a lista novamente. loadMore repassa o evento do virtual scroller para busca paginar. Pela ref, hideDropdown() fecha o painel.

TopTabs

Navegação em abas baseada em Tabs, TabList, Tab, TabPanels e TabPanel do PrimeVue, com estrutura simplificada e estilos dos temas claro e escuro do Design System.

<script setup lang="ts">
import { ref } from 'vue'
import {
  TopTabs,
  type TopTabItem,
  type TopTabValue,
} from '@topjoao/top-design-system'

const activeTab = ref<TopTabValue>('dados')
const tabs: TopTabItem[] = [
  { value: 'dados', label: 'Dados' },
  { value: 'documentos', label: 'Documentos' },
  { value: 'auditoria', label: 'Auditoria', disabled: true },
]
</script>

<template>
  <TopTabs v-model="activeTab" :tabs="tabs">
    <template #dados>
      Dados gerais do processo
    </template>

    <template #documentos>
      Documentos anexados
    </template>

    <template #auditoria>
      Histórico de auditoria
    </template>
  </TopTabs>
</template>
Prop Tipo Padrão
modelValue string | number obrigatório
tabs TopTabItem[] obrigatório
lazy boolean false
scrollable boolean true

Cada TopTabItem possui value, label e disabled?. O value deve ser único e identifica tanto a seleção quanto o slot do painel; por exemplo, value: 'documentos' utiliza #documentos. Cada slot recebe tab e active. O componente emite somente update:modelValue. Atributos adicionais, incluindo as opções de passthrough do PrimeVue, são repassados ao componente Tabs.

Tamanho e espaçamento

Use variáveis CSS para diminuir ou aumentar as abas por instância ou por tema, sem acessar classes internas do PrimeVue. Sem personalização, o tamanho atual é mantido.

Variável CSS Padrão Aplicação
--top-tabs-tab-padding 1rem 1.125rem Espaçamento interno dos botões das abas
--top-tabs-tab-font-size 1rem Tamanho do texto dos botões das abas
--top-tabs-panel-padding 0.875rem 1.125rem 1.125rem Espaçamento interno de cada painel de conteúdo
<!-- Abas compactas -->
<TopTabs
  v-model="activeTab"
  :tabs="tabs"
  style="--top-tabs-tab-padding: 0.5rem 0.75rem; --top-tabs-tab-font-size: 0.875rem; --top-tabs-panel-padding: 0.5rem;"
>
  <!-- slots dos painéis -->
</TopTabs>

<!-- Abas maiores -->
<TopTabs
  v-model="activeTab"
  :tabs="tabs"
  style="--top-tabs-tab-padding: 1.25rem 1.5rem; --top-tabs-tab-font-size: 1.125rem;"
>
  <!-- slots dos painéis -->
</TopTabs>

As variáveis também podem ser declaradas em uma classe CSS ou em um ancestral para definir o tamanho de várias instâncias. O padding de cada painel é independente do padding dos botões e do contêiner TabPanels do PrimeVue.

TopToast

TopToast personaliza o renderizador do serviço Toast do PrimeVue. Registre ToastService uma vez, renderize um único TopToast perto da raiz e dispare mensagens com useToast. Ele não recebe uma lista de mensagens por prop.

// main.ts
import PrimeVue from 'primevue/config'
import ToastService from 'primevue/toastservice'
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).use(PrimeVue).use(ToastService).mount('#app')
<script setup lang="ts">
import { useToast } from 'primevue/usetoast'
import { TopButton, TopToast } from '@topjoao/top-design-system'

const toast = useToast()
const abrirCadastro = () => { /* navegue para o cadastro */ }

function salvar() {
  toast.add({
    severity: 'success', summary: 'Cadastro concluído', detail: 'As alterações foram salvas.', life: 5000,
    data: {
      footer: 'Protocolo: CAD-2026-0042',
      action: { label: 'Ver cadastro', icon: 'pi pi-arrow-right', onClick: () => abrirCadastro() },
    },
  })
}
</script>

<template>
  <TopToast :base-z-index="30000" />
  <TopButton label="Salvar" @click="salvar" />
</template>
Prop Tipo Padrão Finalidade
baseZIndex number 30000 Camada base das notificações.

Além das opções usuais de ToastMessageOptions do PrimeVue (severity, summary, detail, life, closable etc.), data aceita TopToastData:

Campo Tipo Efeito
data.footer string | number Texto discreto abaixo da mensagem.
data.action.label string Rótulo da ação.
data.action.icon string opcional Classe de ícone PrimeIcons.
data.action.onClick () => void opcional Função chamada ao clicar na ação.
Slot Parâmetros recebidos Uso
action { action, message, run } Substitui o botão; execute run() para chamar action.onClick.
footer { message, footer } Substitui o rodapé.

Tipos e exports públicos

O pacote raiz exporta todos os componentes, TopSolutionsPreset, colors e os tipos abaixo. Use import type para não acrescentar código ao bundle.

import {
  colors,
  TopSolutionsPreset,
  type TopButtonSeverity,
  type TopDatePickerSelectionMode,
  type TopDatePickerValue,
  type TopFileUploadError,
  type TopFileUploadValue,
  type TopInputNumberValue,
  type TopInputNumberMode,
  type TopInputNumberButtonLayout,
  type TopInputNumberCurrencyInputMode,
  type TopNavItem,
  type TopNavSection,
  type TopTabItem,
  type TopToastData,
} from '@topjoao/top-design-system'

TopNavItem descreve um nó (id, label, to?, icon?, disabled?, children?, data?); TopNavSection é o mesmo contrato com children obrigatório. TopNavAction, TopNavUser, TopNavClient e TopNavUserMenuItem correspondem às props de mesmo nome. A aparência da barra é definida por TopNavBarAppearance e seus subtipos exportados: TopNavBarColors, TopNavBarSurfaces, TopNavBarBorders, TopNavBarShape e TopNavBarFocus. TopNavBarSpacing tipa as propriedades opcionais headerPadding e breadcrumbPadding da prop spacing.

TopInputNumberValue, TopInputNumberMode, TopInputNumberButtonLayout e TopInputNumberCurrencyInputMode tipam, respectivamente, o valor, o modo de formatação, o layout dos botões e o modo de entrada monetária do campo numérico. colors expõe as escalas imutáveis primary, secondary, success, warn e danger, cada uma com tons de 50 a 950.

Desenvolvimento da biblioteca

npm install
npm run check

O comando check executa verificação de tipos, testes e build.

Para inspecionar o conteúdo que seria publicado:

npm pack --dry-run

Playground visual

O Storybook permite testar os componentes isoladamente e consultar seus exemplos. Ele é uma dependência de desenvolvimento e não é incluído no pacote publicado.

npm run storybook

Abra http://localhost:6006 para acessar as histórias de TopButton, TopConfirmDialog, TopDatePicker, TopInputText, TopTextArea, TopInputNumber, TopSelect e TopTabs. Use o botão de contraste na barra superior para alternar o preview entre tema claro e escuro. Para gerar a versão estática da documentação, execute:

npm run build-storybook

Para gerar um pacote local instalável:

npm pack

Para publicar uma nova versão estável, primeiro incremente a versão seguindo o versionamento semântico; versões já publicadas no npm não podem ser sobrescritas. Use patch, minor ou major conforme o impacto da mudança.

npm version patch --no-git-tag-version
npm publish --access public --tag latest

A tag latest é a padrão do npm. Assim, consumidores instalam a versão estável simplesmente com npm install @topjoao/top-design-system.

Version History

Version Released License
0.2.17 Proprietary
0.2.16 Proprietary
0.2.15 Proprietary
0.2.14 Proprietary
0.2.13 Proprietary
Pre-releases (28)
Version Released License
0.1.8-beta.0 Proprietary
0.1.2-beta.1 Proprietary
0.1.2-beta.0 Proprietary
0.1.1-beta.0 Proprietary
0.1.0-beta.23 Proprietary