@subg-dev/subg-components
v1.4.1
Published
Componentes reutilizáveis da SUBG - Sistema de Sidebar e Breadcrumb
Maintainers
Readme
@subg-dev/subg-components
Componentes de Sidebar e Breadcrumb reutilizáveis baseados em shadcn/ui para Tailwind CSS v4.
📋 Índice
- Visão Geral
- Instalação (Copy-Paste)
- Configuração do Tailwind CSS v4
- Uso Básico
- Customização
- API de Componentes
- Desenvolvimento Local
✨ Visão Geral
Esta biblioteca fornece componentes completos e prontos para uso em aplicações React com Tailwind v4.
🎯 Componentes Principais
- ✅ AppSidebar - Sidebar completa e responsiva com suporte a colapso e navegação hierárquica
- ✅ NavUser - Componente de usuário com avatar, dropdown e ações (Perfil, Configurações, Sair)
- ✅ SidebarFooter - Footer automático com informações de versão, build e usuário
- ✅ NavMain - Navegação vertical integrada com React Router
- ✅ PageBreadcrumb - Breadcrumbs automáticos baseados na URL
- ✅ Badge - Componente de badges com 4 variantes (default, secondary, destructive, outline)
🔧 Utilitários
- ✅ Sistema de Versão - Funções para obter metadata de build (versão, commit SHA, build number, ambiente)
- ✅ Componentes UI base - Todos os primitivos do shadcn/ui necessários (Sidebar, Button, Avatar, Tooltip, etc.)
⚡ Features
- ✅ Footer configurável - 3 modos: com usuário e versão, apenas versão, ou totalmente customizado
- ✅ Navegação hierárquica - Suporta menu com múltiplos níveis de subitens
- ✅ Responsivo - Adapta-se automaticamente para mobile com drawer
- ✅ Modo colapsado - Sidebar pode ser minimizada para ícones
- ✅ Dark mode ready - Variáveis CSS preparadas para tema escuro
- ✅ Tooltips informativos - Build info detalhado com badges de ambiente
Diferencial: Todos os componentes são autônomos e funcionam com Tailwind CSS v4 sem precisar do shadcn/ui instalado.
🚀 Instalação (Copy-Paste)
Seguindo o modelo do shadcn/ui, você copia os componentes diretamente para o seu projeto ao invés de instalar via NPM.
1️⃣ Instalar dependências base
pnpm add @radix-ui/react-avatar @radix-ui/react-collapsible \
@radix-ui/react-dropdown-menu @radix-ui/react-separator \
@radix-ui/react-slot @radix-ui/react-tooltip \
@radix-ui/react-dialog \
class-variance-authority clsx tailwind-merge \
lucide-react react-router-dom2️⃣ Copiar os arquivos de componentes
Copie os seguintes arquivos do repositório para o seu projeto:
📁 Seu Projeto
└── src/
├── components/
│ └── navigation/
│ ├── app-sidebar.tsx ← Sidebar principal
│ ├── nav-main.tsx ← Menu de navegação
│ ├── nav-user.tsx ← Componente de usuário
│ ├── sidebar-footer.tsx ← Footer com versão
│ └── page-breadcrumb.tsx ← Breadcrumbs
│
├── ui/ ← Componentes base shadcn/ui
│ ├── sidebar.tsx
│ ├── separator.tsx
│ ├── button.tsx
│ ├── badge.tsx ← Badges
│ ├── avatar.tsx
│ ├── collapsible.tsx
│ ├── dropdown-menu.tsx
│ ├── tooltip.tsx
│ ├── breadcrumb.tsx
│ ├── sheet.tsx
│ ├── skeleton.tsx
│ └── input.tsx
│
├── lib/
│ └── versao.ts ← Funções de versão/build
│
├── utils/
│ └── cn.ts ← Utilitário de classes
│
└── hooks/
└── use-mobile.ts ← Hook de detecção mobileLink do repositório: github.com/SUBG-RioSaude/subg-components
Você pode clonar o repo e copiar a pasta src/ completa, ou copiar manualmente os arquivos necessários.
🎨 Configuração do Tailwind CSS v4
1️⃣ Adicionar variáveis CSS da Sidebar
No seu arquivo CSS principal (ex: src/index.css ou src/app.css), adicione:
@import 'tailwindcss';
@theme inline {
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
/* Mapeamento de cores para Tailwind */
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
:root {
--radius: 0.625rem;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
/* Variáveis da Sidebar - Light Mode */
--sidebar: oklch(0.404 0.017 264.376); /* Fundo cinza/azulado */
--sidebar-foreground: oklch(1 0 0); /* Texto branco */
--sidebar-primary: oklch(0.709 0.142 213.68); /* #43B9EB - Cor primária */
--sidebar-primary-foreground: oklch(1 0 0); /* Texto branco */
--sidebar-accent: oklch(1 0 0 / 0.1); /* Hover transparente */
--sidebar-accent-foreground: oklch(0.709 0.142 213.68); /* #43B9EB */
--sidebar-border: oklch(0.5 0 0); /* Borda escura */
--sidebar-ring: oklch(0.709 0.142 213.68); /* Anel de foco #43B9EB */
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
/* Variáveis da Sidebar - Dark Mode */
--sidebar: oklch(0.404 0.017 264.376); /* Mantém fundo no dark */
--sidebar-foreground: oklch(1 0 0);
--sidebar-primary: oklch(0.709 0.142 213.68);
--sidebar-primary-foreground: oklch(1 0 0);
--sidebar-accent: oklch(1 0 0 / 0.1);
--sidebar-accent-foreground: oklch(0.709 0.142 213.68);
--sidebar-border: oklch(0.5 0 0);
--sidebar-ring: oklch(0.709 0.142 213.68);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}2️⃣ Configurar tailwind.config.ts (opcional)
O Tailwind v4 não requer plugins para variáveis CSS customizadas. O arquivo pode ficar simples:
export default {
content: ["./src/**/*.{ts,tsx}"],
theme: {
extend: {},
},
}📖 Exemplos de Uso
🟢 Exemplo 1: Básico (Mínimo)
Sidebar simples sem footer, apenas navegação essencial.
import { SidebarProvider, SidebarInset } from '@/ui/sidebar'
import { AppSidebar } from '@/components/navigation/app-sidebar'
import { Home, FileText } from 'lucide-react'
export function BasicLayout({ children }: { children: React.ReactNode }) {
return (
<SidebarProvider>
<AppSidebar
navItems={[
{ title: 'Início', url: '/', icon: Home },
{ title: 'Documentos', url: '/docs', icon: FileText },
]}
logoConfig={{
mainLogoUrl: '/logo.png',
mainLogoAlt: 'Logo',
}}
/>
<SidebarInset>
<main className="p-6">
{children}
</main>
</SidebarInset>
</SidebarProvider>
)
}🟡 Exemplo 2: Intermediário
Sidebar com navegação hierárquica, breadcrumbs e footer com versão.
import { SidebarProvider, SidebarInset, SidebarTrigger } from '@/ui/sidebar'
import { AppSidebar } from '@/components/navigation/app-sidebar'
import { PageBreadcrumb } from '@/components/navigation/page-breadcrumb'
import { Home, Settings, Users, FileText } from 'lucide-react'
import { useNavigate } from 'react-router-dom'
export function IntermediateLayout({ children }: { children: React.ReactNode }) {
const navigate = useNavigate()
const navItems = [
{ title: 'Início', url: '/', icon: Home },
{
title: 'Configurações',
url: '/settings',
icon: Settings,
items: [
{ title: 'Perfil', url: '/settings/profile' },
{ title: 'Preferências', url: '/settings/preferences' },
],
},
{ title: 'Usuários', url: '/users', icon: Users },
{ title: 'Documentos', url: '/docs', icon: FileText },
]
return (
<SidebarProvider>
<AppSidebar
navItems={navItems}
logoConfig={{
mainLogoUrl: '/logo.png',
mainLogoAlt: 'Minha Logo',
badgeText: 'BETA',
logoLink: '/dashboard',
}}
footerConfig={{
showVersion: true,
developerText: 'Desenvolvido com ❤️ pela equipe',
}}
/>
<SidebarInset>
{/* Header com Breadcrumb e Toggle */}
<header className="sticky top-0 z-10 flex h-16 items-center gap-2 border-b bg-white px-4">
<SidebarTrigger />
<PageBreadcrumb
labelMap={{
'settings': 'Configurações',
'users': 'Usuários',
'docs': 'Documentos',
'profile': 'Perfil',
'preferences': 'Preferências',
}}
/>
</header>
{/* Conteúdo Principal */}
<main className="flex-1 overflow-auto p-6">
{children}
</main>
</SidebarInset>
</SidebarProvider>
)
}🔴 Exemplo 3: Completo (Todas as Features)
Sidebar com todas as props, usuário autenticado, versão, badges e navegação completa.
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import {
SidebarProvider,
SidebarInset,
SidebarTrigger
} from '@/ui/sidebar'
import { AppSidebar } from '@/components/navigation/app-sidebar'
import { PageBreadcrumb } from '@/components/navigation/page-breadcrumb'
import { Badge } from '@/ui/badge'
import {
Home,
Settings,
Users,
FileText,
BarChart3,
Package,
CreditCard,
Bell,
HelpCircle,
} from 'lucide-react'
export function CompleteLayout({ children }: { children: React.ReactNode }) {
const navigate = useNavigate()
const [user] = useState({
name: 'Matheus Migliani',
email: '[email protected]',
avatar: '/avatars/matheus.jpg',
})
// Navegação completa com hierarquia
const navItems = [
{
title: 'Dashboard',
url: '/',
icon: Home
},
{
title: 'Relatórios',
url: '/reports',
icon: BarChart3,
items: [
{ title: 'Vendas', url: '/reports/sales' },
{ title: 'Financeiro', url: '/reports/financial' },
{ title: 'Analytics', url: '/reports/analytics' },
],
},
{
title: 'Produtos',
url: '/products',
icon: Package,
items: [
{ title: 'Catálogo', url: '/products/catalog' },
{ title: 'Estoque', url: '/products/inventory' },
{ title: 'Fornecedores', url: '/products/suppliers' },
],
},
{
title: 'Clientes',
url: '/customers',
icon: Users,
items: [
{ title: 'Lista', url: '/customers/list' },
{ title: 'Novo Cliente', url: '/customers/new' },
],
},
{
title: 'Financeiro',
url: '/finance',
icon: CreditCard,
items: [
{ title: 'Faturas', url: '/finance/invoices' },
{ title: 'Pagamentos', url: '/finance/payments' },
{ title: 'Cobranças', url: '/finance/billing' },
],
},
{
title: 'Documentos',
url: '/docs',
icon: FileText
},
{
title: 'Configurações',
url: '/settings',
icon: Settings,
items: [
{ title: 'Perfil', url: '/settings/profile' },
{ title: 'Conta', url: '/settings/account' },
{ title: 'Notificações', url: '/settings/notifications' },
{ title: 'Segurança', url: '/settings/security' },
{ title: 'Integrações', url: '/settings/integrations' },
],
},
]
// Handlers de ações do usuário
const handleLogout = () => {
console.log('Logging out...')
// Lógica de logout
navigate('/login')
}
const handleProfile = () => {
navigate('/settings/profile')
}
const handleSettings = () => {
navigate('/settings')
}
return (
<SidebarProvider defaultOpen={true}>
<AppSidebar
// Navegação
navItems={navItems}
// Logo com badge
logoConfig={{
mainLogoUrl: '/logo-prefeitura.png',
mainLogoAlt: 'Logo Prefeitura do Rio',
badgeText: 'CAC',
badgeLogoUrl: '/logo-cac.png',
logoLink: '/dashboard',
}}
// Footer completo com usuário e versão
footerConfig={{
userConfig: {
user,
onLogout: handleLogout,
onProfile: handleProfile,
onSettings: handleSettings,
},
showVersion: true,
developerText: 'Desenvolvido pela SUBG',
}}
// Props adicionais da Sidebar (shadcn)
variant="sidebar"
collapsible="icon"
className="border-r"
/>
<SidebarInset>
{/* Header com todos os recursos */}
<header className="sticky top-0 z-10 flex h-16 items-center justify-between gap-4 border-b bg-white px-4 shadow-sm">
<div className="flex items-center gap-2">
{/* Toggle da sidebar */}
<SidebarTrigger />
{/* Breadcrumb */}
<PageBreadcrumb
labelMap={{
'reports': 'Relatórios',
'sales': 'Vendas',
'financial': 'Financeiro',
'analytics': 'Analytics',
'products': 'Produtos',
'catalog': 'Catálogo',
'inventory': 'Estoque',
'suppliers': 'Fornecedores',
'customers': 'Clientes',
'list': 'Lista',
'new': 'Novo',
'finance': 'Financeiro',
'invoices': 'Faturas',
'payments': 'Pagamentos',
'billing': 'Cobranças',
'docs': 'Documentos',
'settings': 'Configurações',
'profile': 'Perfil',
'account': 'Conta',
'notifications': 'Notificações',
'security': 'Segurança',
'integrations': 'Integrações',
}}
/>
</div>
{/* Actions no header */}
<div className="flex items-center gap-2">
<Badge variant="outline" className="hidden md:flex">
Ambiente: Produção
</Badge>
<button className="relative p-2 hover:bg-gray-100 rounded-md">
<Bell className="h-5 w-5" />
<span className="absolute top-1 right-1 h-2 w-2 bg-red-500 rounded-full" />
</button>
<button className="p-2 hover:bg-gray-100 rounded-md">
<HelpCircle className="h-5 w-5" />
</button>
</div>
</header>
{/* Conteúdo Principal */}
<main className="flex-1 overflow-auto p-6 bg-gray-50">
{children}
</main>
</SidebarInset>
</SidebarProvider>
)
}🎨 Customização
Alterar cores da Sidebar
Edite as variáveis CSS no seu index.css:
:root {
/* Exemplo: Sidebar verde */
--sidebar: oklch(0.4 0.15 150); /* Fundo verde */
--sidebar-primary: oklch(0.6 0.2 150); /* Verde mais claro */
--sidebar-accent-foreground: oklch(0.6 0.2 150);
}Personalizar a Logo
<AppSidebar
logoConfig={{
mainLogoUrl: '/custom-logo.svg',
mainLogoAlt: 'Logo Customizada',
badgeText: 'BETA', // Texto da badge
badgeLogoUrl: '/badge.png', // Logo da badge (opcional)
logoLink: '/home', // Link ao clicar
}}
navItems={navItems}
/>Footer com Usuário e Versão
A sidebar agora suporta um footer completo com informações de usuário e versão:
Opção 1: Footer automático com footerConfig
<AppSidebar
navItems={navItems}
logoConfig={logoConfig}
footerConfig={{
userConfig: {
user: {
name: 'João Silva',
email: '[email protected]',
avatar: '/avatar.jpg'
},
onLogout: () => handleLogout(),
onProfile: () => navigate('/profile'),
onSettings: () => navigate('/settings'),
},
showVersion: true, // Mostra versão e build info
developerText: 'Desenvolvido pelo time de TI 2025'
}}
/>Opção 2: Apenas versão, sem usuário
<AppSidebar
navItems={navItems}
logoConfig={logoConfig}
footerConfig={{
showVersion: true
}}
/>Opção 3: Footer totalmente customizado
<AppSidebar
navItems={navItems}
logoConfig={logoConfig}
footerContent={
<div className="p-4 space-y-2">
<button className="w-full btn-primary">Sair</button>
<p className="text-xs text-center">v1.0.0</p>
</div>
}
/>Configurar Variáveis de Versão
O footer com showVersion: true exibe informações de build obtidas de variáveis de ambiente.
1. Crie um arquivo .env na raiz do projeto:
VITE_APP_VERSION=1.0.0
VITE_COMMIT_SHA=abc1234
VITE_BUILD_NUMBER=42
VITE_BUILD_TIMESTAMP=2025-10-21T12:00:00Z
VITE_AMBIENTE=production2. Configure no CI/CD (exemplo GitHub Actions):
- name: Build
env:
VITE_APP_VERSION: ${{ github.ref_name }}
VITE_COMMIT_SHA: ${{ github.sha }}
VITE_BUILD_NUMBER: ${{ github.run_number }}
VITE_BUILD_TIMESTAMP: ${{ github.event.head_commit.timestamp }}
VITE_AMBIENTE: production
run: pnpm buildSe as variáveis não forem definidas, o sistema usa valores padrão (versão 1.0.0, build "local", etc.).
📚 API de Componentes
AppSidebar
| Prop | Tipo | Descrição |
|------|------|-----------|
| navItems | NavItem[] | Lista de itens de navegação (requerido) |
| logoConfig | LogoConfig | Configuração da logo (requerido) |
| footerConfig | SidebarFooterProps | Configuração automática do footer com versão e usuário (opcional) |
| footerContent | ReactNode | Conteúdo totalmente customizado do footer (opcional, alternativa ao footerConfig) |
NavItem
interface NavItem {
title: string
url: string
icon: LucideIcon
items?: { title: string; url: string }[] // Subitens opcionais
}LogoConfig
interface LogoConfig {
mainLogoUrl: string // URL da logo principal
mainLogoAlt?: string // Texto alternativo
badgeLogoUrl?: string // URL da logo da badge
badgeText?: string // Texto da badge (ex: "CAC")
logoLink?: string // Link ao clicar na logo
}SidebarFooterProps
interface SidebarFooterProps {
userConfig?: {
user?: {
name: string
email: string
avatar?: string
}
onLogout?: () => void
onProfile?: () => void
onSettings?: () => void
}
showVersion?: boolean // Padrão: true
developerText?: string // Texto customizado do desenvolvedor
}NavUser
Componente standalone para exibir usuário com dropdown de ações.
| Prop | Tipo | Descrição |
|------|------|-----------|
| user | { name, email, avatar? } | Dados do usuário (opcional, mostra placeholder se omitido) |
| onLogout | () => void | Callback ao clicar em "Sair" (opcional) |
| onProfile | () => void | Callback ao clicar em "Perfil" (opcional) |
| onSettings | () => void | Callback ao clicar em "Configurações" (opcional) |
Badge
Componente de badge baseado em shadcn/ui.
| Prop | Tipo | Valores | Descrição |
|------|------|---------|-----------|
| variant | string | default, secondary, destructive, outline | Variante visual do badge |
Exemplo:
<Badge variant="default">Novo</Badge>
<Badge variant="destructive">Erro</Badge>PageBreadcrumb
| Prop | Tipo | Descrição |
|------|------|-----------|
| labelMap | Record<string, string> | Mapeia segmentos da URL para labels legíveis (opcional) |
| items | BreadcrumbItemType[] | Lista manual de breadcrumbs (opcional) |
SidebarProvider
| Prop | Tipo | Descrição |
|------|------|-----------|
| defaultOpen | boolean | Estado inicial da sidebar (padrão: true) |
| open | boolean | Controle externo do estado (opcional) |
| onOpenChange | (open: boolean) => void | Callback ao mudar estado (opcional) |
Funções de Versão
// Obtém versão da aplicação
obterVersaoApp(): string
// Obtém ano atual
obterAnoAtual(): number
// Obtém metadata completa
obterMetadataVersao(): MetadataVersao
interface MetadataVersao {
versao: string
buildNumber: string
commitSha: string
buildTimestamp: string
ambiente: 'development' | 'staging' | 'production'
}🏗️ Desenvolvimento Local
Se você quiser contribuir ou modificar a biblioteca:
# Clonar o repositório
git clone https://github.com/SUBG-RioSaude/subg-components.git
cd subg-components
# Instalar dependências
pnpm install
# Build
pnpm build
# Watch mode (desenvolvimento)
pnpm dev📦 Distribuição via NPM (Legado)
⚠️ Nota: A instalação via NPM não é mais recomendada devido a incompatibilidades com Tailwind v4. Use o método copy-paste descrito acima.
Se ainda assim quiser instalar via NPM:
pnpm add @subg-dev/subg-componentsVocê precisará importar o CSS manualmente:
@import '@subg-dev/subg-components/styles.css';📄 Licença
MIT License · SUBG - Subsecretaria de Gestão
🤝 Contribuindo
Contribuições são bem-vindas! Abra uma issue ou pull request no repositório GitHub.
Desenvolvido com ❤️ pela equipe SUBG
