@grazziotin/react-components-next 0.1.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,174 +1,266 @@
1
- # React Components Library
2
-
3
- Biblioteca de componentes React reutilizáveis, construída com TypeScript e Tailwind CSS.
4
-
5
- ## Instalação
6
-
7
- ```bash
8
- npm install react-components-next
9
- ```
10
-
11
- > **Atenção:** esta biblioteca usa classes do Tailwind CSS. Certifique-se de que o Tailwind esteja configurado no seu projeto.
12
-
13
- ## Componentes disponíveis
14
-
15
- | Componente | Descrição |
16
- |------------|-----------|
17
- | `Button` | Botão com variantes, tamanhos, loading e ícones |
18
- | `Input` | Campo de entrada com label, hint, erro e addons |
19
- | `Card` | Container com header, content e footer compostos |
20
- | `Badge` | Etiqueta colorida para status e categorias |
21
- | `Avatar` | Foto de perfil com fallback de iniciais |
22
-
23
- ## Uso
24
-
25
- ```tsx
26
- import { Button, Card, CardHeader, CardTitle, CardContent, Input, Badge, Avatar } from "react-components-next";
27
-
28
- export function Example() {
29
- return (
30
- <Card>
31
- <CardHeader>
32
- <CardTitle>Perfil</CardTitle>
33
- </CardHeader>
34
- <CardContent>
35
- <div className="flex items-center gap-3">
36
- <Avatar fallback="João Silva" size="md" />
37
- <div>
38
- <p>João Silva</p>
39
- <Badge variant="success" dot>Online</Badge>
40
- </div>
41
- </div>
42
- <Input label="E-mail" type="email" placeholder="joao@exemplo.com" />
43
- <Button variant="primary" fullWidth>Salvar</Button>
44
- </CardContent>
45
- </Card>
46
- );
47
- }
48
- ```
49
-
50
- ## Componentes
51
-
52
- ### Button
53
-
54
- ```tsx
55
- <Button
56
- variant="primary" // "primary" | "secondary" | "outline" | "ghost" | "destructive"
57
- size="md" // "sm" | "md" | "lg"
58
- loading={false} // boolean
59
- fullWidth={false} // boolean
60
- leftIcon={<Icon />} // ReactNode
61
- rightIcon={<Icon />} // ReactNode
62
- >
63
- Clique aqui
64
- </Button>
65
- ```
66
-
67
- ### Input
68
-
69
- ```tsx
70
- <Input
71
- label="Nome"
72
- placeholder="Digite..."
73
- hint="Texto de ajuda"
74
- error="Mensagem de erro"
75
- size="md" // "sm" | "md" | "lg"
76
- leftAddon={<Icon />} // ReactNode
77
- rightAddon={<Icon />} // ReactNode
78
- fullWidth={false}
79
- />
80
- ```
81
-
82
- ### Card
83
-
84
- ```tsx
85
- <Card shadow="sm" padding="md" hoverable bordered>
86
- <CardHeader>
87
- <CardTitle>Título</CardTitle>
88
- <CardDescription>Descrição</CardDescription>
89
- </CardHeader>
90
- <CardContent>Conteúdo</CardContent>
91
- <CardFooter>
92
- <Button>Ação</Button>
93
- </CardFooter>
94
- </Card>
95
- ```
96
-
97
- ### Badge
98
-
99
- ```tsx
100
- <Badge
101
- variant="success" // "default" | "primary" | "success" | "warning" | "danger" | "info"
102
- dot={false} // boolean - exibe um ponto colorido
103
- >
104
- Online
105
- </Badge>
106
- ```
107
-
108
- ### Avatar
109
-
110
- ```tsx
111
- <Avatar
112
- src="https://..." // URL da imagem (opcional)
113
- alt="Nome do usuário"
114
- fallback="João Silva" // usado para gerar as iniciais
115
- size="md" // "xs" | "sm" | "md" | "lg" | "xl"
116
- />
117
- ```
118
-
119
- ## Desenvolvimento
120
-
121
- ```bash
122
- # Instalar dependências
123
- npm install
124
-
125
- # Rodar servidor de desenvolvimento com showcase
126
- npm run dev
127
-
128
- # Build da biblioteca para npm
129
- npm run build:lib
130
-
131
- # Build completo (biblioteca + Next.js)
132
- npm run build:all
133
-
134
- # Verificar tipos TypeScript
135
- npm run type-check
136
- ```
137
-
138
- ## Publicar no npm
139
-
140
- ```bash
141
- # Fazer login no npm
142
- npm login
143
-
144
- # Publicar (executa build:lib automaticamente via prepublishOnly)
145
- npm publish
146
- ```
147
-
148
- ## Estrutura do projeto
149
-
150
- ```
151
- src/
152
- ├── app/ # Next.js App Router (showcase)
153
- │ ├── page.tsx # Página inicial com lista de componentes
154
- │ └── showcase/ # Páginas de demonstração
155
- │ ├── button/
156
- │ ├── input/
157
- │ ├── card/
158
- │ ├── badge/
159
- │ └── avatar/
160
- ├── components/ # Biblioteca de componentes (publicada no npm)
161
- │ ├── index.ts # Entrypoint principal
162
- │ └── ui/
163
- │ ├── Button/
164
- │ ├── Input/
165
- │ ├── Card/
166
- │ ├── Badge/
167
- │ └── Avatar/
168
- └── lib/
169
- └── utils.ts # Utilitário cn() para classes CSS
170
- ```
171
-
172
- ## Licença
173
-
174
- MIT
1
+ # Grazziotin React Components Library
2
+
3
+ Biblioteca de componentes React reutilizáveis para o projeto Grazziotin, construída com TypeScript, Tailwind CSS, Material UI e Mantine.
4
+
5
+ ## Instalação
6
+
7
+ ```bash
8
+ npm install @grazziotin/react-components-next
9
+ ```
10
+
11
+ ### Requisitos
12
+
13
+ - React `>= 18`
14
+ - Tailwind CSS v4 configurado no projeto consumidor
15
+ - `ThemeProvider` do MUI e `MantineProvider` do Mantine envolvendo a aplicação
16
+
17
+ ### Configuração no projeto consumidor
18
+
19
+ Importe os estilos da biblioteca e configure as variáveis CSS do tema:
20
+
21
+ ```tsx
22
+ import "@grazziotin/react-components-next/styles";
23
+
24
+ import { CssBaseline, ThemeProvider, createTheme } from "@mui/material";
25
+ import { MantineProvider } from "@mantine/core";
26
+ import "@mantine/core/styles.css";
27
+
28
+ const theme = createTheme({
29
+ typography: { fontFamily: "var(--font-family, inherit)" },
30
+ });
31
+
32
+ export function AppProviders({ children }: { children: React.ReactNode }) {
33
+ return (
34
+ <ThemeProvider theme={theme}>
35
+ <CssBaseline />
36
+ <MantineProvider>{children}</MantineProvider>
37
+ </ThemeProvider>
38
+ );
39
+ }
40
+ ```
41
+
42
+ ```css
43
+ :root {
44
+ --primary-color: #00b2a6;
45
+ --font-family: "Poppins", sans-serif;
46
+ }
47
+ ```
48
+
49
+ > **Atenção:** vários componentes usam classes do Tailwind CSS e dependem do MUI/Mantine. Certifique-se de que o Tailwind esteja configurado e os providers estejam na raiz da aplicação.
50
+
51
+ ## Componentes disponíveis
52
+
53
+ | Componente | Descrição |
54
+ | ------------ | --------------------------------------------------------------- |
55
+ | `Card` | Container com cabeçalho colorido, título, ícone e tooltip |
56
+ | `Dialog` | Modal baseado no MUI com título, conteúdo e ações opcionais |
57
+ | `DataTable` | Tabela de dados com MUI DataGrid, filtros e textos em português |
58
+ | `Tab`/`Tabs` | Abas estilizadas com indicador e tipografia customizáveis |
59
+
60
+ ## Funções utilitárias
61
+
62
+ | Função | Descrição |
63
+ | --------------- | ------------------------------------------------ |
64
+ | `cn` | Mescla classes CSS com `clsx` e `tailwind-merge` |
65
+ | `nvl` | Retorna valor padrão quando `null`/`undefined` |
66
+ | `formatCpfCnpj` | Formata CPF ou CNPJ |
67
+ | `formatPhoneBr` | Formata telefone brasileiro |
68
+
69
+ ## Uso
70
+
71
+ ```tsx
72
+ import {
73
+ Card,
74
+ Dialog,
75
+ DataTable,
76
+ Tab,
77
+ Tabs,
78
+ cn,
79
+ formatCpfCnpj,
80
+ } from "@grazziotin/react-components-next";
81
+ import type { GridColDef } from "@mui/x-data-grid";
82
+
83
+ const colunas: GridColDef[] = [
84
+ { field: "id", headerName: "ID", width: 80 },
85
+ { field: "nome", headerName: "Nome", flex: 1 },
86
+ { field: "documento", headerName: "CPF/CNPJ", width: 180 },
87
+ ];
88
+
89
+ const linhas = [
90
+ { id: 1, nome: "Ana Silva", documento: formatCpfCnpj("12345678901") },
91
+ ];
92
+
93
+ export function Example() {
94
+ return (
95
+ <Card title="Colaboradores" toolTip className={cn("max-w-3xl")}>
96
+ <DataTable rows={linhas} columns={colunas} pageSizeOptions={[10, 20]} />
97
+ </Card>
98
+ );
99
+ }
100
+ ```
101
+
102
+ ## Entry points
103
+
104
+ A biblioteca expõe múltiplos pontos de entrada:
105
+
106
+ | Import | Conteúdo |
107
+ | --------------------------------------------- | ---------------------------------------- |
108
+ | `@grazziotin/react-components-next` | Componentes e funções (export principal) |
109
+ | `@grazziotin/react-components-next/ui` | Apenas componentes de UI |
110
+ | `@grazziotin/react-components-next/functions` | Apenas funções utilitárias |
111
+ | `@grazziotin/react-components-next/styles` | CSS compilado da biblioteca |
112
+
113
+ ## Componentes
114
+
115
+ ### Card
116
+
117
+ ```tsx
118
+ <Card
119
+ title="Título do card"
120
+ toolTip={false}
121
+ width="100%"
122
+ height="auto"
123
+ titleColor="var(--primary-color)"
124
+ borderRadius="10px"
125
+ borderTitle="10px 10px 0 0"
126
+ icon={<Icon />}
127
+ onClick={() => {}}
128
+ className="text-sm"
129
+ >
130
+ Conteúdo do card
131
+ </Card>
132
+ ```
133
+
134
+ ### Dialog
135
+
136
+ ```tsx
137
+ <Dialog
138
+ open={open}
139
+ title="Confirmar exclusão"
140
+ onClose={() => setOpen(false)}
141
+ maxWidth="sm"
142
+ blurBackdrop={false}
143
+ actions={<button onClick={() => setOpen(false)}>OK</button>}
144
+ >
145
+ Deseja realmente excluir este item?
146
+ </Dialog>
147
+ ```
148
+
149
+ ### DataTable
150
+
151
+ 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`.
152
+
153
+ ```tsx
154
+ <DataTable
155
+ rows={dados}
156
+ columns={colunas}
157
+ loading={carregando}
158
+ pageSizeOptions={[10, 20, 50]}
159
+ />
160
+ ```
161
+
162
+ ### Tab / Tabs
163
+
164
+ ```tsx
165
+ const [value, setValue] = useState(0);
166
+
167
+ <Tabs
168
+ value={value}
169
+ onChange={(_, newValue) => setValue(newValue)}
170
+ color="var(--primary-color)"
171
+ >
172
+ <Tab label="Geral" />
173
+ <Tab label="Detalhes" />
174
+ </Tabs>;
175
+ ```
176
+
177
+ ## Funções
178
+
179
+ ### cn
180
+
181
+ ```tsx
182
+ cn("px-4 py-2", condicao && "bg-teal-500", "px-6");
183
+ ```
184
+
185
+ ### nvl
186
+
187
+ ```tsx
188
+ nvl(valor, "padrão"); // retorna "padrão" se valor for null/undefined
189
+ ```
190
+
191
+ ### formatCpfCnpj
192
+
193
+ ```tsx
194
+ formatCpfCnpj("12345678901"); // "123.456.789-01"
195
+ formatCpfCnpj("12345678000199"); // "12.345.678/0001-99"
196
+ ```
197
+
198
+ ### formatPhoneBr
199
+
200
+ ```tsx
201
+ formatPhoneBr("11987654321"); // "(11) 98765-4321"
202
+ formatPhoneBr("1133334444"); // "(11) 3333-4444"
203
+ ```
204
+
205
+ ## Desenvolvimento
206
+
207
+ ```bash
208
+ # Instalar dependências
209
+ npm install
210
+
211
+ # Storybook (visualização e documentação dos componentes)
212
+ npm run storybook
213
+
214
+ # Build estático do Storybook
215
+ npm run build-storybook
216
+
217
+ # Build da biblioteca para npm
218
+ npm run build:lib
219
+
220
+ # Verificar tipos TypeScript
221
+ npm run type-check
222
+
223
+ # Lint
224
+ npm run lint
225
+ ```
226
+
227
+ ## Publicar no npm
228
+
229
+ ```bash
230
+ # Fazer login no npm
231
+ npm login
232
+
233
+ # Publicar (executa build:lib automaticamente via prepublishOnly)
234
+ npm publish
235
+ ```
236
+
237
+ ## Estrutura do projeto
238
+
239
+ ```
240
+ src/
241
+ ├── app/ # Next.js App Router (página inicial mínima)
242
+ │ ├── globals.css # Variáveis CSS e tema
243
+ │ ├── layout.tsx # Providers MUI + Mantine
244
+ │ └── page.tsx
245
+ ├── components/
246
+ │ ├── index.ts
247
+ │ └── ui/
248
+ │ ├── card/
249
+ │ ├── dialog/
250
+ │ ├── data-table/
251
+ │ └── tab/
252
+ ├── core/ # Utilitários internos
253
+ ├── functions/
254
+ │ ├── cn/
255
+ │ ├── nvl/
256
+ │ ├── format-cpf-cnpj/
257
+ │ └── format-phone-br/
258
+ ├── providers/
259
+ ├── styles/
260
+ │ └── index.css # Entrada do Tailwind para o build CSS
261
+ └── index.ts # Entrypoint principal da biblioteca
262
+ ```
263
+
264
+ ## Licença
265
+
266
+ MIT
@@ -0,0 +1,79 @@
1
+ import { twMerge } from 'tailwind-merge';
2
+ import { clsx } from 'clsx';
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __defProps = Object.defineProperties;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
+ var __objRest = (source, exclude) => {
24
+ var target = {};
25
+ for (var prop in source)
26
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
27
+ target[prop] = source[prop];
28
+ if (source != null && __getOwnPropSymbols)
29
+ for (var prop of __getOwnPropSymbols(source)) {
30
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
31
+ target[prop] = source[prop];
32
+ }
33
+ return target;
34
+ };
35
+ function cn(...values) {
36
+ return twMerge(clsx(values));
37
+ }
38
+
39
+ // src/functions/nvl/nvl.ts
40
+ function nvl(value, defaultValue) {
41
+ return value != null ? value : defaultValue;
42
+ }
43
+
44
+ // src/core/remove-digits.ts
45
+ function removeDigits(value) {
46
+ return value.replace(/\D/g, "");
47
+ }
48
+
49
+ // src/functions/format-cpf-cnpj/format-cpf-cnpj.ts
50
+ function formatCpfCnpj(data) {
51
+ if (!data) return "";
52
+ const value = removeDigits(data);
53
+ if (!value) return "";
54
+ if (value.length <= 11) {
55
+ const cpf = value.slice(0, 11);
56
+ return cpf.replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d{1,2})$/, "$1-$2");
57
+ }
58
+ const cnpj = value.slice(0, 14);
59
+ return cnpj.replace(/^(\d{2})(\d)/, "$1.$2").replace(/^(\d{2})\.(\d{3})(\d)/, "$1.$2.$3").replace(/\.(\d{3})(\d)/, ".$1/$2").replace(/(\d{4})(\d)/, "$1-$2");
60
+ }
61
+
62
+ // src/functions/format-phone-br/format-phone-br.ts
63
+ function formatPhoneBr(phone) {
64
+ if (!phone) return "";
65
+ const digits = removeDigits(phone).slice(0, 11);
66
+ if (!digits) return "";
67
+ if (digits.length <= 2) return digits;
68
+ if (digits.length <= 6) {
69
+ return digits.replace(/(\d{2})(\d+)/, "($1) $2");
70
+ }
71
+ if (digits.length <= 10) {
72
+ return digits.replace(/(\d{2})(\d{4})(\d+)/, "($1) $2-$3");
73
+ }
74
+ return digits.replace(/(\d{2})(\d{5})(\d+)/, "($1) $2-$3");
75
+ }
76
+
77
+ export { __objRest, __spreadProps, __spreadValues, cn, formatCpfCnpj, formatPhoneBr, nvl };
78
+ //# sourceMappingURL=chunk-4DIPDYEU.mjs.map
79
+ //# sourceMappingURL=chunk-4DIPDYEU.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/functions/cn/cn.ts","../src/functions/nvl/nvl.ts","../src/core/remove-digits.ts","../src/functions/format-cpf-cnpj/format-cpf-cnpj.ts","../src/functions/format-phone-br/format-phone-br.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIO,SAAS,MAAM,MAAA,EAA8B;AAClD,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;;;ACIO,SAAS,GAAA,CAAO,OAA6B,YAAA,EAAoB;AACtE,EAAA,OAAO,KAAA,IAAA,IAAA,GAAA,KAAA,GAAS,YAAA;AAClB;;;ACXO,SAAS,aAAa,KAAA,EAAuB;AAClD,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAChC;;;ACOO,SAAS,cAAc,IAAA,EAAuB;AACnD,EAAA,IAAI,CAAC,MAAM,OAAO,EAAA;AAClB,EAAA,MAAM,KAAA,GAAQ,aAAa,IAAI,CAAA;AAE/B,EAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AACnB,EAAA,IAAI,KAAA,CAAM,UAAU,EAAA,EAAI;AACtB,IAAA,MAAM,GAAA,GAAM,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAC7B,IAAA,OAAO,GAAA,CACJ,OAAA,CAAQ,aAAA,EAAe,OAAO,CAAA,CAC9B,OAAA,CAAQ,aAAA,EAAe,OAAO,CAAA,CAC9B,OAAA,CAAQ,mBAAA,EAAqB,OAAO,CAAA;AAAA,EACzC;AAEA,EAAA,MAAM,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAC9B,EAAA,OAAO,IAAA,CACJ,OAAA,CAAQ,cAAA,EAAgB,OAAO,EAC/B,OAAA,CAAQ,uBAAA,EAAyB,UAAU,CAAA,CAC3C,QAAQ,eAAA,EAAiB,QAAQ,CAAA,CACjC,OAAA,CAAQ,eAAe,OAAO,CAAA;AACnC;;;ACnBO,SAAS,cAAc,KAAA,EAAwB;AACpD,EAAA,IAAI,CAAC,OAAO,OAAO,EAAA;AACnB,EAAA,MAAM,SAAS,YAAA,CAAa,KAAK,CAAA,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AAE9C,EAAA,IAAI,CAAC,QAAQ,OAAO,EAAA;AACpB,EAAA,IAAI,MAAA,CAAO,MAAA,IAAU,CAAA,EAAG,OAAO,MAAA;AAC/B,EAAA,IAAI,MAAA,CAAO,UAAU,CAAA,EAAG;AACtB,IAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,cAAA,EAAgB,SAAS,CAAA;AAAA,EACjD;AACA,EAAA,IAAI,MAAA,CAAO,UAAU,EAAA,EAAI;AACvB,IAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,qBAAA,EAAuB,YAAY,CAAA;AAAA,EAC3D;AACA,EAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,qBAAA,EAAuB,YAAY,CAAA;AAC3D","file":"chunk-4DIPDYEU.mjs","sourcesContent":["import { twMerge } from \"tailwind-merge\";\r\nimport { clsx } from \"clsx\";\r\nimport type { ClassValue } from \"./utils/interfaces\";\r\n\r\nexport function cn(...values: ClassValue[]): string {\r\n return twMerge(clsx(values));\r\n}\r\n","/**\r\n * Função NVL (Null Value Logic) - retorna valor padrão se o valor for null/undefined\r\n * @param {T | null | undefined} value - Valor a ser verificado\r\n * @param {T} defaultValue - Valor padrão a ser retornado se value for null/undefined\r\n * @returns {T} Value se não for null/undefined, senão defaultValue\r\n * @template T - Tipo do valor\r\n * @example\r\n * nvl(null, 'padrão') // Retorna: 'padrão'\r\n * nvl('valor', 'padrão') // Retorna: 'valor'\r\n */\r\nexport function nvl<T>(value: T | null | undefined, defaultValue: T): T {\r\n return value ?? defaultValue;\r\n}\r\n","/** Remove tudo que não for dígito. */\r\nexport function removeDigits(value: string): string {\r\n return value.replace(/\\D/g, \"\");\r\n}\r\n","import { removeDigits } from \"../../core/remove-digits\";\r\n\r\n/**\r\n * Formata CPF (até 11 dígitos) ou CNPJ (12–14 dígitos) conforme o tamanho.\r\n * @param data - Valor com ou sem formatação\r\n * @returns CPF `000.000.000-00` ou CNPJ `00.000.000/0000-00`; string vazia se ausente\r\n * @example\r\n * formatCpfCnpj(\"12345678901\") // \"123.456.789-01\"\r\n * formatCpfCnpj(\"12345678000199\") // \"12.345.678/0001-99\"\r\n */\r\nexport function formatCpfCnpj(data?: string): string {\r\n if (!data) return \"\";\r\n const value = removeDigits(data);\r\n\r\n if (!value) return \"\";\r\n if (value.length <= 11) {\r\n const cpf = value.slice(0, 11);\r\n return cpf\r\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\r\n .replace(/(\\d{3})(\\d)/, \"$1.$2\")\r\n .replace(/(\\d{3})(\\d{1,2})$/, \"$1-$2\");\r\n }\r\n\r\n const cnpj = value.slice(0, 14);\r\n return cnpj\r\n .replace(/^(\\d{2})(\\d)/, \"$1.$2\")\r\n .replace(/^(\\d{2})\\.(\\d{3})(\\d)/, \"$1.$2.$3\")\r\n .replace(/\\.(\\d{3})(\\d)/, \".$1/$2\")\r\n .replace(/(\\d{4})(\\d)/, \"$1-$2\");\r\n}\r\n","import { removeDigits } from \"../../core/remove-digits\";\r\n\r\n/**\r\n * Formata telefone brasileiro (até 11 dígitos): fixo `(00) 0000-0000` ou celular `(00) 00000-0000`.\r\n * @param phone - Valor com ou sem formatação\r\n * @returns Telefone formatado ou string vazia se ausente\r\n * @example\r\n * formatPhoneBr(\"11987654321\") // \"(11) 98765-4321\"\r\n * formatPhoneBr(\"1133334444\") // \"(11) 3333-4444\"\r\n */\r\nexport function formatPhoneBr(phone?: string): string {\r\n if (!phone) return \"\";\r\n const digits = removeDigits(phone).slice(0, 11);\r\n\r\n if (!digits) return \"\";\r\n if (digits.length <= 2) return digits;\r\n if (digits.length <= 6) {\r\n return digits.replace(/(\\d{2})(\\d+)/, \"($1) $2\");\r\n }\r\n if (digits.length <= 10) {\r\n return digits.replace(/(\\d{2})(\\d{4})(\\d+)/, \"($1) $2-$3\");\r\n }\r\n return digits.replace(/(\\d{2})(\\d{5})(\\d+)/, \"($1) $2-$3\");\r\n}\r\n"]}