diff --git a/docs/formatters/cpf.md b/docs/formatters/cpf.md new file mode 100644 index 0000000..6b9d5d2 --- /dev/null +++ b/docs/formatters/cpf.md @@ -0,0 +1,97 @@ +# Utilitários de Formatação de CPF + +Este módulo fornece funções para aplicar e remover a máscara de CPF (Cadastro de Pessoas Físicas) em strings, facilitando a manipulação e exibição de documentos no padrão brasileiro. + +## Instalação e Importação + +```typescript +import { maskCpf, removeCpfMask } from '@sysvale/foundry'; +``` + +## Funções + +### `maskCpf()` + +Aplica a máscara de CPF no formato `XXX.XXX.XXX-XX` a uma string contendo apenas números. + +#### Sintaxe + +```typescript +maskCpf(value: string): string +``` + +#### Parâmetros + +- **`value`** (`string`): String contendo apenas números ou um CPF já formatado. + +#### Retorno + +`string` – O CPF formatado no padrão `XXX.XXX.XXX-XX` se a entrada tiver 11 dígitos. Caso contrário, retorna apenas os dígitos informados. + +#### Exemplos + +```typescript +maskCpf('11111111111'); // → '111.111.111-11' +maskCpf(''); // → '' +maskCpf('1234567890'); // → '1234567890' (menos de 11 dígitos, retorna sem máscara) +``` + +#### Casos de Uso + +- Exibir CPF formatado em formulários e relatórios. +- Garantir consistência visual de documentos em interfaces de usuário. + +--- + +### `removeCpfMask()` + +Remove qualquer máscara de CPF, retornando apenas os dígitos numéricos. + +#### Sintaxe + +```typescript +removeCpfMask(value: string): string +``` + +#### Parâmetros + +- **`value`** (`string`): String contendo um CPF formatado ou não. + +#### Retorno + +`string` – String contendo apenas os números do CPF. + +#### Exemplos + +```typescript +removeCpfMask('111.111.111-11'); // → '11111111111' +removeCpfMask(''); // → '' +removeCpfMask('1234567890'); // → '1234567890' (não altera se não houver máscara) +``` + +#### Casos de Uso + +- Armazenar CPFs em banco de dados sem formatação. +- Validar ou comparar CPFs independentemente do formato de entrada. + +## Casos de Uso Comuns + +### Formatação para exibição + +```typescript +const cpf = '12345678901'; +const formatted = maskCpf(cpf); // '123.456.789-01' +``` + +### Remoção de máscara para validação + +```typescript +const masked = '123.456.789-01'; +const digits = removeCpfMask(masked); // '12345678901' +``` + +## Limitações + +- As funções não validam se o CPF é válido, apenas formatam ou removem a máscara. +- Se a string de entrada não tiver 11 dígitos, `maskCpf` retorna apenas os dígitos sem aplicar a máscara. +- Caracteres não numéricos são removidos por `removeCpfMask`. diff --git a/docs/formatters/index.md b/docs/formatters/index.md new file mode 100644 index 0000000..679d012 --- /dev/null +++ b/docs/formatters/index.md @@ -0,0 +1,17 @@ +# Utilitários de Formatação + +Este módulo fornece funções para formatar diversos tipos de dados. + +## Formatadores + +### CPF + +Funções para aplicação ou remoção de máscaras em CPFs. + +- [Documentação](./cpf.md) + +### Telefone + +Funções para aplicação ou remoção de máscaras em números telefônicos brasileiros. + +- [Documentação](./phone.md) diff --git a/docs/formatters/phone.md b/docs/formatters/phone.md new file mode 100644 index 0000000..d22f6ac --- /dev/null +++ b/docs/formatters/phone.md @@ -0,0 +1,110 @@ +# Utilitários de Formatação de Telefone + +Este módulo fornece funções para aplicar e remover a máscara de números de telefone brasileiros, facilitando a manipulação e exibição de telefones fixos e celulares no padrão nacional. + +## Instalação e Importação + +```typescript +import { maskPhone, removePhoneMask } from '@sysvale/foundry'; +``` + +## Funções + +### `maskPhone()` + +Aplica a máscara de telefone brasileiro no formato `(XX) XXXXX-XXXX` para celulares (11 dígitos) ou `(XX) XXXX-XXXX` para fixos (10 dígitos). + +#### Sintaxe + +```typescript +maskPhone(value: string): string +``` + +#### Parâmetros + +- **`value`** (`string`): String contendo apenas números, telefone já formatado ou com caracteres especiais. + +#### Retorno + +`string` – O telefone formatado no padrão nacional, conforme a quantidade de dígitos. Se a entrada não tiver 10 ou 11 dígitos, retorna apenas os dígitos informados. + +#### Exemplos + + +```typescript +maskPhone('11987654321'); // → '(11) 98765-4321' +maskPhone('1187654321'); // → '(11) 8765-4321' +maskPhone(''); // → '' +maskPhone('123456789'); // → '123456789' (menos de 10 dígitos, retorna sem máscara) +maskPhone('(11) 98765-4321'); // → '(11) 98765-4321' (já formatado) +maskPhone('11.98765.4321'); // → '(11) 98765-4321' +maskPhone('11abc98765def4321');// → '(11) 98765-4321' +maskPhone('1'); // → '1' +``` + +#### Casos de Uso + +- Exibir telefones formatados em formulários e relatórios. +- Garantir consistência visual de números de telefone em interfaces de usuário. + +--- + +### `removePhoneMask()` + +Remove qualquer máscara de telefone, retornando apenas os dígitos numéricos. + +#### Sintaxe + +```typescript +removePhoneMask(value: string): string +``` + +#### Parâmetros + +- **`value`** (`string`): String contendo um telefone formatado ou não. + +#### Retorno + +`string` – String contendo apenas os números do telefone. + +#### Exemplos + + +```typescript +removePhoneMask('(11) 98765-4321'); // → '11987654321' +removePhoneMask('(11) 8765-4321'); // → '1187654321' +removePhoneMask(''); // → '' +removePhoneMask('11987654321'); // → '11987654321' (sem máscara, retorna igual) +removePhoneMask('11 98765 4321'); // → '11987654321' +removePhoneMask('11.98765.4321'); // → '11.98765.4321' (pontos não são removidos) +removePhoneMask('11#98765#4321'); // → '11#98765#4321' (outros caracteres não são removidos) +removePhoneMask('(11) 98765-4321 ext.123'); // → '11987654321ext.123' +``` + +#### Casos de Uso + +- Armazenar telefones em banco de dados sem formatação. +- Validar ou comparar telefones independentemente do formato de entrada. +- Remover apenas os caracteres de máscara padrão: parênteses, hífens e espaços. + +## Casos de Uso Comuns + +### Formatação para exibição + +```typescript +const phone = '11987654321'; +const formatted = maskPhone(phone); // '(11) 98765-4321' +``` + +### Remoção de máscara para validação + +```typescript +const masked = '(11) 98765-4321'; +const digits = removePhoneMask(masked); // '11987654321' +``` + +## Limitações + +- As funções não validam se o número de telefone é válido, apenas formatam ou removem a máscara. +- Se a string de entrada não tiver 10 ou 11 dígitos, `maskPhone` retorna apenas os dígitos sem aplicar a máscara. +- `removePhoneMask` remove apenas parênteses, hífens e espaços; outros caracteres especiais permanecem. diff --git a/docs/index.md b/docs/index.md index 798d96c..a0d9309 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,8 +8,11 @@ hero: features: - title: Utils - details: Métodos utilitários em TypesCript para facilitar tarefas comuns no front-end. + details: Métodos utilitários em TypeScript para facilitar tarefas comuns no front-end. link: /utils/pluralize + - title: Formatadores + details: Métodos em TypeScript para formatar diversos tipos de dados. + link: /formatters/index - title: Composables details: Composables Vue 3 reutilizáveis para lógica comum no front-end. link: /composables/ diff --git a/package.json b/package.json index b8b9db8..a94c6e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sysvale/foundry", - "version": "1.0.0", + "version": "1.1.0", "description": "A forge for composables, helpers, and front-end utilities.", "type": "module", "main": "./dist/foundry.cjs.js", diff --git a/src/formatters/cpf.ts b/src/formatters/cpf.ts new file mode 100644 index 0000000..ec06861 --- /dev/null +++ b/src/formatters/cpf.ts @@ -0,0 +1,27 @@ +const maskCpf = (value: string | number) => { + let cpf = typeof value === 'number' ? value.toString() : value; + + if (!cpf) { + return ''; + } + + cpf = cpf.replace(/\D/g, ''); + + if (cpf.length === 11) { + return `${cpf.substring(0, 3)}.${cpf.substring(3, 6)}.${cpf.substring(6, 9)}-${cpf.substring(9, 11)}`; + } + + return cpf; +}; + +const removeCpfMask = (value: string | number) => { + let cpf = typeof value === 'number' ? value.toString() : value; + + if (!cpf) { + return ''; + } + + return cpf.replace(/\D/g, ''); +}; + +export { maskCpf, removeCpfMask }; diff --git a/src/formatters/phone.ts b/src/formatters/phone.ts new file mode 100644 index 0000000..0b068ce --- /dev/null +++ b/src/formatters/phone.ts @@ -0,0 +1,30 @@ +const maskPhone = (value: string | null | number | undefined) => { + let phone = typeof value === 'number' ? value.toString() : value; + + if (!phone || phone.length === 0) { + return ''; + } + + phone = phone.replace(/[^0-9]+/g, ''); + + if (phone.length === 11) { + return `(${phone.substring(0, 2)}) ${phone.substring(2, 7)}-${phone.substring(7, 11)}`; + } + + if (phone.length === 10) { + return `(${phone.substring(0, 2)}) ${phone.substring(2, 6)}-${phone.substring(6, 10)}`; + } + return phone; +}; + +const removePhoneMask = (value: string | null | number | undefined) => { + let phone = typeof value === 'number' ? value.toString() : value; + + if (!phone || phone.length === 0) { + return ''; + } + + return phone.replace(/\(|\)|-|\s/g, ''); +}; + +export { maskPhone, removePhoneMask }; diff --git a/src/index.ts b/src/index.ts index b206d0f..3cdaeab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,3 @@ export * from './utils/pluralize'; +export { maskCpf, removeCpfMask } from './formatters/cpf'; +export { maskPhone, removePhoneMask } from './formatters/phone'; diff --git a/tests/cpf.test.ts b/tests/cpf.test.ts new file mode 100644 index 0000000..11385d0 --- /dev/null +++ b/tests/cpf.test.ts @@ -0,0 +1,31 @@ +import { describe, test, expect } from 'vitest'; +import { maskCpf, removeCpfMask } from '../src/formatters/cpf'; + +describe('maskCpf', () => { + test('deve formatar cpf', () => { + expect(maskCpf('11111111111')).toBe('111.111.111-11'); + expect(maskCpf(11111111111)).toBe('111.111.111-11'); + }); + + test('deve retornar string vazia quando cpf é vazio', () => { + expect(maskCpf('')).toBe(''); + }); + + test('deve retornar valor como está quando cpf é inválido', () => { + expect(maskCpf('1234567890')).toBe('1234567890'); + }); +}); + +describe('removeCpfMask', () => { + test('deve remover máscara de cpf', () => { + expect(removeCpfMask('111.111.111-11')).toBe('11111111111'); + }); + + test('deve retornar string vazia quando cpf é vazio', () => { + expect(removeCpfMask('')).toBe(''); + }); + + test('deve retornar valor como está quando cpf é inválido', () => { + expect(removeCpfMask('1234567890')).toBe('1234567890'); + }); +}); diff --git a/tests/phone.test.ts b/tests/phone.test.ts new file mode 100644 index 0000000..6672758 --- /dev/null +++ b/tests/phone.test.ts @@ -0,0 +1,86 @@ +import { describe, test, expect } from 'vitest'; +import { maskPhone, removePhoneMask } from '../src/formatters/phone'; + +describe('maskPhone', () => { + test('deve formatar número de telefone com 11 dígitos (com 9)', () => { + expect(maskPhone('11987654321')).toBe('(11) 98765-4321'); + expect(maskPhone(11987654321)).toBe('(11) 98765-4321'); + }); + + test('deve formatar número de telefone com 10 dígitos (sem 9)', () => { + expect(maskPhone('1187654321')).toBe('(11) 8765-4321'); + expect(maskPhone(1187654321)).toBe('(11) 8765-4321'); + }); + + test('deve retornar string vazia quando o telefone é vazio', () => { + expect(maskPhone('')).toBe(''); + }); + + test('deve retornar string vazia quando o telefone é nulo/undefined', () => { + expect(maskPhone(null)).toBe(''); + expect(maskPhone(undefined)).toBe(''); + }); + + test('deve retornar apenas dígitos quando o telefone tem comprimento inválido', () => { + expect(maskPhone('123456789')).toBe('123456789'); + expect(maskPhone('123456789012')).toBe('123456789012'); + }); + + test('deve remover caracteres não numéricos antes de formatar', () => { + expect(maskPhone('(11) 98765-4321')).toBe('(11) 98765-4321'); + expect(maskPhone('11.98765.4321')).toBe('(11) 98765-4321'); + expect(maskPhone('11 98765 4321')).toBe('(11) 98765-4321'); + }); + + test('deve lidar com telefone com letras e caracteres especiais', () => { + expect(maskPhone('11abc98765def4321')).toBe('(11) 98765-4321'); + expect(maskPhone('11@98765#4321')).toBe('(11) 98765-4321'); + }); + + test('deve lidar com casos de números curtos', () => { + expect(maskPhone('1')).toBe('1'); + expect(maskPhone('11')).toBe('11'); + expect(maskPhone('119')).toBe('119'); + }); +}); + +describe('removePhoneMask', () => { + test('deve remover máscara de número de telefone com 11 dígitos', () => { + expect(removePhoneMask('(11) 98765-4321')).toBe('11987654321'); + }); + + test('deve remover máscara de número de telefone com 10 dígitos', () => { + expect(removePhoneMask('(11) 8765-4321')).toBe('1187654321'); + }); + + test('deve retornar string vazia quando o telefone é vazio', () => { + expect(removePhoneMask('')).toBe(''); + }); + + test('deve retornar string vazia quando o telefone é nulo/undefined', () => { + expect(removePhoneMask(null)).toBe(''); + expect(removePhoneMask(undefined)).toBe(''); + }); + + test('deve retornar valor como está quando o telefone não tem máscara', () => { + expect(removePhoneMask('11987654321')).toBe('11987654321'); + expect(removePhoneMask('1187654321')).toBe('1187654321'); + }); + + test('deve remover apenas caracteres de máscara específicos', () => { + expect(removePhoneMask('(11) 98765-4321')).toBe('11987654321'); + expect(removePhoneMask('11 98765 4321')).toBe('11987654321'); + expect(removePhoneMask('(11)98765-4321')).toBe('11987654321'); + }); + + test('deve preservar caracteres que não são de máscara', () => { + expect(removePhoneMask('11.98765.4321')).toBe('11.98765.4321'); + expect(removePhoneMask('11#98765#4321')).toBe('11#98765#4321'); + }); + + test('deve lidar com caracteres de máscara e não-máscara misturados', () => { + expect(removePhoneMask('(11) 98765-4321 ext.123')).toBe( + '11987654321ext.123' + ); + }); +}); diff --git a/tests/pluralize.test.ts b/tests/pluralize.test.ts index a187600..776b86c 100644 --- a/tests/pluralize.test.ts +++ b/tests/pluralize.test.ts @@ -3,37 +3,37 @@ import { pluralize, pluralizeWords, pluralizeWithCount, -} from '../src/utils/pluralize'; // Adjust the path +} from '../src/utils/pluralize'; describe('pluralize()', () => { - test('returns same word if count is 1', () => { + test('retorna a mesma palavra se a quantidade for 1', () => { expect(pluralize(1, 'carro')).toBe('carro'); }); - test('adds "s" for regular words ending in vowel', () => { + test('adiciona "s" para palavras regulares terminadas em vogal', () => { expect(pluralize(2, 'banana')).toBe('bananas'); }); - test('uses custom plural if provided', () => { + test('usa o plural personalizado se fornecido', () => { expect(pluralize(2, 'livro')).not.toBe('livrozinhos'); expect(pluralize(2, 'livro', 'livrozinhos')).toBe('livrozinhos'); }); - test('does not change words ending in s or x', () => { + test('não altera palavras terminadas em s ou x', () => { expect(pluralize(3, 'tórax')).toBe('tórax'); expect(pluralize(3, 'lápis')).toBe('lápis'); }); - test('converts -ão to -ões', () => { + test('converte -ão para -ões', () => { expect(pluralize(2, 'avião')).toBe('aviões'); }); - test('converts some irregulars properly', () => { + test('converte alguns irregulares corretamente', () => { expect(pluralize(2, 'pão')).toBe('pães'); expect(pluralize(2, 'alemão')).toBe('alemães'); }); - test('convert custom irregulars properly', () => { + test('converte irregulares customizados corretamente', () => { expect(pluralize(2, 'órgão')).toBe('órgãos'); expect(pluralize(2, 'cônsul')).not.toBe('cônsules'); expect(pluralize(2, 'cônsul', undefined, { cônsul: 'cônsules' })).toBe( @@ -42,74 +42,74 @@ describe('pluralize()', () => { expect(pluralize(2, 'maçã', undefined, { maçã: 'maçãs' })).toBe('maçãs'); }); - test('converts -m to -ns', () => { + test('converte -m para -ns', () => { expect(pluralize(2, 'homem')).toBe('homens'); }); - test('adds "es" for words ending in r or z', () => { + test('adiciona "es" para palavras terminadas em r ou z', () => { expect(pluralize(2, 'luz')).toBe('luzes'); expect(pluralize(2, 'motor')).toBe('motores'); }); - test('converts-ol to -óis', () => { + test('converte -ol para -óis', () => { expect(pluralize(2, 'sol')).toBe('sóis'); }); - test('converts -al, -ul to -ais, -uis', () => { + test('converte -al, -ul para -ais, -uis', () => { expect(pluralize(2, 'animal')).toBe('animais'); expect(pluralize(2, 'azul')).toBe('azuis'); }); - test('converts -el to -éis', () => { + test('converte -el para -éis', () => { expect(pluralize(2, 'papel')).toBe('papéis'); }); - test('converts -il to -is', () => { + test('converte -il para -is', () => { expect(pluralize(2, 'fuzil')).toBe('fuzis'); }); - test('uses plural when count is negative', () => { + test('usa o plural quando a quantidade é negativa', () => { expect(pluralize(-3, 'avião')).toBe('aviões'); }); - test('does not use plural when count is zero', () => { + test('não usa o plural quando a quantidade é zero', () => { expect(pluralize(0, 'mapa')).toBe('mapa'); }); - test('defaults count to 2 if null or undefined', () => { + test('usa o padrão de 2 se a quantidade for nula ou indefinida', () => { expect(pluralize(null, 'livro')).toBe('livros'); }); - test('treats first param as word if count is a string', () => { + test('trata o primeiro parâmetro como palavra se a quantidade for uma string', () => { expect(pluralize('carro', '')).toBe('carros'); }); }); describe('pluralizeWithCount()', () => { - test('includes the count in the result', () => { + test('inclui a quantidade no resultado', () => { expect(pluralizeWithCount(1, 'carro')).toBe('1 carro'); expect(pluralizeWithCount(2, 'carro')).toBe('2 carros'); }); - test('works with custom plural', () => { + test('funciona com plural personalizado', () => { expect(pluralizeWithCount(2, 'livro', 'livrozinhos')).toBe('2 livrozinhos'); }); }); describe('pluralizeWords()', () => { - test('works with one word', () => { + test('funciona com uma palavra', () => { expect(pluralizeWords(0, ['papel'])).toBe('papel'); expect(pluralizeWords(1, ['papel'])).toBe('papel'); expect(pluralizeWords(2, ['papel'])).toBe('papéis'); }); - test('works with two words', () => { + test('funciona com duas palavras', () => { expect(pluralizeWords(0, ['avião', 'antigo'])).toBe('avião antigo'); expect(pluralizeWords(1, ['avião', 'antigo'])).toBe('avião antigo'); expect(pluralizeWords(2, ['avião', 'antigo'])).toBe('aviões antigos'); }); - test('works with three words', () => { + test('funciona com três palavras', () => { expect(pluralizeWords(0, ['moto', 'branca', 'escolhida'])).toBe( 'moto branca escolhida' ); @@ -121,7 +121,7 @@ describe('pluralizeWords()', () => { ); }); - test('works with custom plurals', () => { + test('funciona com plurais personalizados', () => { expect( pluralizeWords( 2,