sistemasintegrados-ui
v0.1.1
Published
Design system padrão dos sistemas integrados
Readme
sistemasintegrados-ui
Design system padrão dos Sistemas Integrados — componentes React construídos sobre Radix UI e Tailwind CSS v4, com suporte nativo a dark mode.
Instalação
npm install sistemasintegrados-uiCSS (obrigatório)
Importe o CSS uma única vez na raiz da sua aplicação (ex.: main.tsx, app.tsx ou o arquivo de estilos global):
import 'sistemasintegrados-ui/styles'O CSS contém:
- Tokens de cor para temas claro e escuro (variáveis CSS HSL)
- Tipografia: Inter (sans-serif) e JetBrains Mono (monospace) via Google Fonts
- Tokens de
border-radius, sombras, espaçamento e tracking - Utilitários de elevação (
hover-elevate,toggle-elevate) para estados interativos sem mudar cor de fundo - Customização de scrollbars alinhada ao tema
- Suporte a
.darke[data-theme="dark"]
Configuração do Tailwind
O pacote usa Tailwind CSS v4. Para que as classes do design system sejam geradas corretamente no projeto consumidor, aponte o scanner para o dist do pacote.
Com @import "tailwindcss" no CSS (v4 puro):
@import "tailwindcss";
@source "../node_modules/sistemasintegrados-ui/dist";Com arquivo tailwind.config.ts (v3/v4 compat):
export default {
content: [
'./src/**/*.{ts,tsx}',
'./node_modules/sistemasintegrados-ui/dist/**/*.{js,cjs}',
],
darkMode: 'class',
}Tema escuro
Adicione a classe .dark ou o atributo data-theme="dark" no elemento raiz:
document.documentElement.classList.add('dark')
// ou
document.documentElement.setAttribute('data-theme', 'dark')Componentes
Inputs
Button
import { Button } from 'sistemasintegrados-ui'
<Button>Salvar</Button>
<Button variant="outline">Cancelar</Button>
<Button variant="destructive" size="sm">Excluir</Button>
<Button variant="ghost" size="icon"><PlusIcon /></Button>
<Button disabled>Indisponível</Button>| Prop | Valores |
|------|---------|
| variant | default · secondary · outline · ghost · link · destructive |
| size | default · sm · lg · icon |
| asChild | boolean — renderiza o filho como elemento raiz (útil com <Link>) |
Input / Textarea
import { Input, Textarea, Label } from 'sistemasintegrados-ui'
<Label htmlFor="nome">Nome</Label>
<Input id="nome" placeholder="Digite o nome..." />
<Label htmlFor="obs">Observações</Label>
<Textarea id="obs" rows={4} placeholder="Detalhes..." />Checkbox / RadioGroup
import { Checkbox, Label, RadioGroup, RadioGroupItem } from 'sistemasintegrados-ui'
<div className="flex items-center gap-2">
<Checkbox id="ativo" />
<Label htmlFor="ativo">Ativo</Label>
</div>
<RadioGroup defaultValue="op1">
<div className="flex items-center gap-2">
<RadioGroupItem value="op1" id="op1" />
<Label htmlFor="op1">Opção 1</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="op2" id="op2" />
<Label htmlFor="op2">Opção 2</Label>
</div>
</RadioGroup>Select
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from 'sistemasintegrados-ui'
<Select>
<SelectTrigger>
<SelectValue placeholder="Selecione..." />
</SelectTrigger>
<SelectContent>
<SelectItem value="a">Opção A</SelectItem>
<SelectItem value="b">Opção B</SelectItem>
</SelectContent>
</Select>MultiSelectCombobox
Dropdown com busca e seleção múltipla via checkboxes.
import { MultiSelectCombobox, type MultiSelectOption } from 'sistemasintegrados-ui'
const options: MultiSelectOption[] = [
{ value: 'ti', label: 'TI' },
{ value: 'rh', label: 'RH' },
]
<MultiSelectCombobox
options={options}
value={selected}
onChange={setSelected}
placeholder="Selecionar setores..."
/>MultipleSelector
Seletor múltiplo com tags removíveis, busca assíncrona e limite de seleções.
import MultipleSelector, { type Option } from 'sistemasintegrados-ui'
<MultipleSelector
defaultOptions={options}
value={value}
onChange={setValue}
placeholder="Adicionar responsáveis..."
maxSelected={3}
onMaxSelected={(max) => alert(`Máximo de ${max} seleções`)}
/>Suporta opções com color (exibe bolinha de status antes do label) e busca assíncrona via onSearch.
ColorPicker
Seletor de tema de cor com paleta pré-definida. Salva a preferência no localStorage.
import { ColorPicker } from 'sistemasintegrados-ui'
<ColorPicker />Display
Badge
import { Badge } from 'sistemasintegrados-ui'
<Badge>Padrão</Badge>
<Badge variant="success">Concluído</Badge>
<Badge variant="warning">Pendente</Badge>
<Badge variant="destructive">Cancelado</Badge>
<Badge variant="info">Em andamento</Badge>
<Badge variant="secondary">Rascunho</Badge>
<Badge variant="outline">Outline</Badge>7 variantes: default · secondary · outline · success · warning · destructive · info
Card
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from 'sistemasintegrados-ui'
<Card>
<CardHeader>
<CardTitle>Título</CardTitle>
<CardDescription>Subtítulo ou descrição breve.</CardDescription>
</CardHeader>
<CardContent>
Conteúdo principal do card.
</CardContent>
<CardFooter>
<Button>Ação</Button>
</CardFooter>
</Card>Avatar
import { Avatar, AvatarImage, AvatarFallback } from 'sistemasintegrados-ui'
<Avatar>
<AvatarImage src="/foto.jpg" alt="Guilherme" />
<AvatarFallback>GP</AvatarFallback>
</Avatar>Table
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from 'sistemasintegrados-ui'
<Table>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>Nome</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>001</TableCell>
<TableCell>Instalação de rede</TableCell>
<TableCell><Badge variant="success">Concluído</Badge></TableCell>
</TableRow>
</TableBody>
</Table>Progress / Skeleton / FilterBadge
import { Progress, Skeleton, FilterBadge } from 'sistemasintegrados-ui'
<Progress value={72} />
<Skeleton className="h-4 w-48" />
<FilterBadge label="Status: Aberto" onRemove={() => clearFilter('status')} />Navigation
Breadcrumb
import {
Breadcrumb, BreadcrumbList, BreadcrumbItem,
BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator,
} from 'sistemasintegrados-ui'
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem><BreadcrumbLink href="/">Início</BreadcrumbLink></BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem><BreadcrumbLink href="/ordens">Ordens</BreadcrumbLink></BreadcrumbItem>
<BreadcrumbSeparator />
<BreadcrumbItem><BreadcrumbPage>OS #1042</BreadcrumbPage></BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>Tabs
import { Tabs, TabsList, TabsTrigger, TabsContent } from 'sistemasintegrados-ui'
<Tabs defaultValue="detalhes">
<TabsList>
<TabsTrigger value="detalhes">Detalhes</TabsTrigger>
<TabsTrigger value="historico">Histórico</TabsTrigger>
</TabsList>
<TabsContent value="detalhes">...</TabsContent>
<TabsContent value="historico">...</TabsContent>
</Tabs>Separator
import { Separator } from 'sistemasintegrados-ui'
<Separator /> {/* horizontal */}
<Separator orientation="vertical" /> {/* vertical */}Overlay
Dialog
import {
Dialog, DialogTrigger, DialogContent,
DialogHeader, DialogTitle, DialogFooter,
} from 'sistemasintegrados-ui'
<Dialog>
<DialogTrigger asChild>
<Button>Abrir</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Confirmar ação</DialogTitle>
</DialogHeader>
<p>Deseja continuar?</p>
<DialogFooter>
<Button>Confirmar</Button>
</DialogFooter>
</DialogContent>
</Dialog>AlertDialog
Modal de confirmação com ação destrutiva.
import {
AlertDialog, AlertDialogTrigger, AlertDialogContent,
AlertDialogHeader, AlertDialogTitle, AlertDialogDescription,
AlertDialogFooter, AlertDialogAction, AlertDialogCancel,
} from 'sistemasintegrados-ui'
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive">Excluir</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Tem certeza?</AlertDialogTitle>
<AlertDialogDescription>Esta ação não pode ser desfeita.</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancelar</AlertDialogCancel>
<AlertDialogAction>Excluir</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>DropdownMenu
import {
DropdownMenu, DropdownMenuTrigger, DropdownMenuContent,
DropdownMenuItem, DropdownMenuSeparator,
} from 'sistemasintegrados-ui'
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon"><MoreHorizontal /></Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>Editar</DropdownMenuItem>
<DropdownMenuItem>Duplicar</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem className="text-destructive">Excluir</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>Command (paleta de busca)
import {
Command, CommandInput, CommandList,
CommandGroup, CommandItem, CommandEmpty,
} from 'sistemasintegrados-ui'
<Command>
<CommandInput placeholder="Buscar..." />
<CommandList>
<CommandEmpty>Nenhum resultado.</CommandEmpty>
<CommandGroup heading="Ações">
<CommandItem>Nova OS</CommandItem>
<CommandItem>Relatório</CommandItem>
</CommandGroup>
</CommandList>
</Command>Popover
import { Popover, PopoverTrigger, PopoverContent } from 'sistemasintegrados-ui'
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Filtros</Button>
</PopoverTrigger>
<PopoverContent className="w-80">
Conteúdo do popover
</PopoverContent>
</Popover>Feedback
Toast
Baseado em react-hot-toast. Adicione o <Toaster /> na raiz e use toast.* em qualquer lugar.
import { toast } from 'sistemasintegrados-ui'
import { Toaster } from 'react-hot-toast'
// Na raiz da app:
<Toaster position="top-right" />
// Em qualquer componente:
toast.success('Ordem salva com sucesso!')
toast.error('Falha ao enviar.')
toast.warning('Campos incompletos.')
toast.info('Sincronização em andamento.')
// Notificação rica com título e ação:
toast.notification({
title: 'Nova OS atribuída',
message: 'OS #1042 foi atribuída a você.',
type: 'info',
action: { label: 'Ver', onClick: () => navigate('/ordens/1042') },
})AnimatedToastProvider
Provider alternativo com animações via Framer Motion e suporte a módulos de notificação (OS, Atendimento, Comentários).
import { AnimatedToastProvider, useAnimatedToast } from 'sistemasintegrados-ui'
// Na raiz:
<AnimatedToastProvider position="top-right">
<App />
</AnimatedToastProvider>
// Em qualquer componente:
const { showToast } = useAnimatedToast()
showToast({ message: 'Atualizado!', type: 'success' })NotificationInboxPopover
Sino de notificações in-app com badge de não lidas, feed de itens e integração com a API do backend.
import { NotificationInboxPopover } from 'sistemasintegrados-ui'
<NotificationInboxPopover
usuarioId={user.id}
initialUnreadCount={sharedData.unreadCount}
clientId={transmitClientId}
/>Requer backend com endpoints
/api/notificacoese SSE via@adonisjs/transmit.
FileUploadProgress
Área de upload com drag-and-drop, barra de progresso por arquivo e tabela de itens enviados.
import { FileUploadProgress, type FileUploadItem } from 'sistemasintegrados-ui'
<FileUploadProgress
files={files}
onFilesAdded={handleFilesAdded}
onRemove={handleRemove}
/>DateTime
DatePicker / DateTimePicker
import { DatePicker, DateTimePicker } from 'sistemasintegrados-ui'
<DatePicker
value={date}
onChange={setDate}
placeholder="Selecione uma data"
/>
<DateTimePicker
value={datetime}
onChange={setDatetime}
/>DateRangePicker
Seletor de intervalo com calendário duplo e presets em pt-BR (Hoje, Últimos 7 dias, Este mês, etc.).
import { DateRangePicker } from 'sistemasintegrados-ui'
<DateRangePicker
from={filters.dataInicio}
to={filters.dataFim}
onChange={({ from, to }) => setFilters({ dataInicio: from, dataFim: to })}
/>Layout
ScrollArea
import { ScrollArea } from 'sistemasintegrados-ui'
<ScrollArea className="h-72">
{longContent}
</ScrollArea>Utilitários
import { cn, formatDateTime, formatDateBr, formatTimeBr, getInitials, getCsrfHeaders } from 'sistemasintegrados-ui'
cn('px-4 py-2', isActive && 'bg-primary') // merge de classes Tailwind
formatDateTime('2025-11-26T06:33:00Z') // "26/11/2025 às 06:33:00"
formatDateBr('2025-11-26') // "26/11/2025"
formatTimeBr('2025-11-26T14:30:00Z') // "14:30"
getInitials('Guilherme Patuci') // "GP"
getCsrfHeaders() // { 'X-XSRF-TOKEN': '...' }Locale pt-BR para calendários
import { calendarDateFnsLocalePt, presetsPt } from 'sistemasintegrados-ui'
// Usar em <Calendar locale={calendarDateFnsLocalePt} />
// presetsPt = array com Hoje, Ontem, Últimos 7 dias, Últimos 30 dias, Este mês, Mês passado, Este ano