Biblioteca de componentes React reutilizáveis para o projeto Grazziotin, construída com TypeScript, Tailwind CSS, Material UI e Mantine.
npm install @grazziotin/react-components-nextInstale também as peer dependencies necessárias:
npm install @mui/material @mui/x-data-grid @emotion/react @emotion/styled @mantine/core @mantine/hooks use-immerPara usar o Drawer (shadcn/Base UI), instale também:
npm install @base-ui/reactPara usar useQueryAuth, instale também:
npm install @tanstack/react-query- React
>= 18 - Material UI (
@mui/material,@mui/x-data-grid) e Emotion - Mantine (
@mantine/core,@mantine/hooks) GrazziotinProviders(ouThemeProviderdo MUI +MantineProvider) envolvendo a aplicação
Configure os providers na raiz da aplicação. Os estilos Tailwind da biblioteca são carregados automaticamente ao importar componentes de /ui ou do entry point raiz.
Carregue também a fonte Poppins e os estilos do Mantine:
import "@mantine/core/styles.css";
import { GrazziotinProviders } from "@grazziotin/react-components-next/providers";No HTML ou CSS global do projeto consumidor:
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap"
/>:root {
--primary-color: #00b2a6;
--font-family: "Poppins", sans-serif;
}export function AppProviders({ children }: { children: React.ReactNode }) {
return <GrazziotinProviders>{children}</GrazziotinProviders>;
}Atenção: a biblioteca referencia
Poppinsvia variáveis CSS, mas não embute o arquivo da fonte — o projeto consumidor precisa carregá-la. Os estilos do Mantine (@mantine/core/styles.css) também são obrigatórios.
| Componente | Descrição |
|---|---|
Card |
Container com cabeçalho colorido, título, ícone e tooltip |
Dialog |
Modal baseado no MUI com título, conteúdo e ações opcionais |
DataTable |
Tabela de dados com MUI DataGrid, filtros e textos em português |
Tab/Tabs |
Abas estilizadas com indicador e tipografia customizáveis |
Input |
Campo de texto com máscaras e estilização MUI |
InputSelect |
Select com grid de opções |
Filter |
Drawer de filtros com campos configuráveis |
ButtonQuantity |
Controle de quantidade (− / input / +) |
Drawer |
Bottom/side sheet composto (Base UI), com floating ou snap |
No iOS Safari, o overlay do Drawer exige no CSS global do app:
body {
position: relative;
}Os estilos Tailwind do Drawer vêm injetados com o bundle /ui — não precisa configurar Tailwind nem CSS extra no consumidor (além do body acima, se for iOS).
| Export | Descrição |
|---|---|
Say |
Componente de leitura de tela (toast) |
useSay |
Hook para disparar feedback por voz |
| Função | Descrição |
|---|---|
cn |
Mescla classes CSS com clsx e tailwind-merge |
nvl |
Retorna valor padrão quando null/undefined |
formatCpfCnpj |
Formata CPF ou CNPJ |
formatPhoneBr |
Formata telefone brasileiro |
formatPriceBrl |
Formata valor monetário em BRL |
formatItem170 |
Formata item 170 |
formatItem150 |
Formata item 150 |
shortName |
Extrai o primeiro e o último nome |
removeDigits |
Mantém apenas dígitos |
removeNonDigits |
Remove todos os dígitos |
removeTextOnly |
Mantém apenas letras |
formatElapsedSince |
Formata tempo decorrido como d HH:MM:SS |
| Hook | Descrição |
|---|---|
useNow |
Retorna o timestamp atual atualizado a cada intervalo |
useDebounce |
Retorna o valor com debounce |
useQueryAuth |
useQuery que só roda quando autenticado |
import {
Card,
DataTable,
cn,
formatCpfCnpj,
} from "@grazziotin/react-components-next";
import type { GridColDef } from "@mui/x-data-grid";
const colunas: GridColDef[] = [
{ field: "id", headerName: "ID", width: 80 },
{ field: "nome", headerName: "Nome", flex: 1 },
{ field: "documento", headerName: "CPF/CNPJ", width: 180 },
];
const linhas = [
{ id: 1, nome: "Ana Silva", documento: formatCpfCnpj("12345678901") },
];
export function Example() {
return (
<Card title="Colaboradores" toolTip className={cn("max-w-3xl")}>
<DataTable rows={linhas} columns={colunas} pageSizeOptions={[10, 20]} />
</Card>
);
}A biblioteca expõe múltiplos pontos de entrada:
| Import | Conteúdo |
|---|---|
@grazziotin/react-components-next |
Componentes, funções, hooks, acessibilidade e providers |
@grazziotin/react-components-next/ui |
Apenas componentes de UI |
@grazziotin/react-components-next/functions |
Apenas funções utilitárias |
@grazziotin/react-components-next/hooks |
Hooks (useNow, useDebounce, useQueryAuth) |
@grazziotin/react-components-next/accessibility |
Componentes de acessibilidade (Say, useSay) |
@grazziotin/react-components-next/providers |
GrazziotinProviders, AuthProvider, NavigationProvider |
@grazziotin/react-components-next/styles |
CSS compilado da biblioteca (opcional — já incluído ao importar /ui ou .) |
<Card
title="Título do card"
toolTip={false}
width="100%"
height="auto"
titleColor="var(--primary-color)"
borderRadius="10px"
borderTitle="10px 10px 0 0"
icon={<Icon />}
onClick={() => {}}
className="text-sm"
>
Conteúdo do card
</Card><Dialog
open={open}
title="Confirmar exclusão"
onClose={() => setOpen(false)}
maxWidth="sm"
blurBackdrop={false}
actions={<button onClick={() => setOpen(false)}>OK</button>}
>
Deseja realmente excluir este item?
</Dialog>Wrapper sobre o MUI DataGrid com estilização, textos em português e operador de filtro "entre" automático para colunas string e number. Aceita todas as props do DataGrid.
<DataTable
rows={dados}
columns={colunas}
loading={carregando}
pageSizeOptions={[10, 20, 50]}
/>const [value, setValue] = useState(0);
<Tabs
value={value}
onChange={(_, newValue) => setValue(newValue)}
color="var(--primary-color)"
>
<Tab label="Geral" />
<Tab label="Detalhes" />
</Tabs>;import { ButtonQuantity } from "@grazziotin/react-components-next/ui";
const [qty, setQty] = useState(1);
<ButtonQuantity
quantity={qty}
minValue={0}
maxValue={99}
size="lg"
model="soft"
radius={10}
onChangeQuantity={setQty}
/>;cn("px-4 py-2", condicao && "bg-teal-500", "px-6");nvl(valor, "padrão"); // retorna "padrão" se valor for null/undefinedformatCpfCnpj("12345678901"); // "123.456.789-01"
formatCpfCnpj("12345678000199"); // "12.345.678/0001-99"formatPhoneBr("11987654321"); // "(11) 98765-4321"
formatPhoneBr("1133334444"); // "(11) 3333-4444"shortName("flavia roseane alves de souza"); // "flavia souza"
shortName("João Silva"); // "João Silva"
shortName("Maria"); // "Maria"removeDigits("(11) 98765-4321"); // "11987654321"
removeNonDigits("ABC-12.34"); // "ABC-."
removeTextOnly("João 123!"); // "João"formatElapsedSince(
"2026-08-06T10:00:00.000Z",
Date.parse("2026-08-06T11:01:05.000Z"),
);
// "0 01:01:05"import { useNow } from "@grazziotin/react-components-next/hooks";
import { formatElapsedSince } from "@grazziotin/react-components-next/functions";
const now = useNow();
formatElapsedSince(order.createdAt, now);import { useDebounce } from "@grazziotin/react-components-next/hooks";
const debouncedSearch = useDebounce(search, 300);import { useQueryAuth } from "@grazziotin/react-components-next/hooks";
import { AuthProvider } from "@grazziotin/react-components-next/providers";
<AuthProvider token={sessionToken}>
<App />
</AuthProvider>;
const { data } = useQueryAuth({
queryKey: ["orders"],
queryFn: fetchOrders,
});MIT