maskarajs 1.0.0 → 1.0.2

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,94 +1,143 @@
1
- # mask.js
1
+ # maskarajs
2
2
 
3
- Engine de máscaras declarativo para JavaScript — framework-agnostic, zero dependências, ~4kb.
3
+ Declarative input mask engine for JavaScript. Framework-agnostic, zero dependencies, and small enough to live close to your form fields.
4
4
 
5
- ## Sintaxe
5
+ ## Syntax
6
6
 
7
- | Token | Aceita |
7
+ | Token | Accepts |
8
8
  |---|---|
9
- | `#` | qualquer dígito (09) |
10
- | `@` | qualquer letra (az, AZ, acentuados) |
11
- | `*` | qualquer caractere |
12
- | `[texto]` | literal fixo (inserido/removido automaticamente) |
13
- | `{expr}` | slot livre testa um char contra a expressão |
9
+ | `#` | any digit (`0-9`) |
10
+ | `@` | any letter (`a-z`, `A-Z`, accented letters) |
11
+ | `*` | any character |
12
+ | `[text]` | fixed literal text, inserted and removed automatically |
13
+ | `{expr}` | free slot: tests one character against an expression |
14
+
15
+ ### `{expr}` modifier
16
+
17
+ ```txt
18
+ {4} -> only the char "4"
19
+ {0-4} -> range: ch >= "0" && ch <= "4"
20
+ {013} -> set: "0", "1" or "3"
21
+ {[0-9a-f]} -> regex: any hexadecimal char
22
+ {\d} -> regex: any digit
23
+ {[^aeiou]} -> regex: any consonant
24
+ ```
14
25
 
15
- ### Modificador `{expr}`
26
+ ## Install
16
27
 
28
+ ```bash
29
+ npm install maskarajs
17
30
  ```
18
- {4} → só o char '4'
19
- {0-4} → intervalo: ch >= '0' && ch <= '4'
20
- {013} → conjunto: '0', '1' ou '3'
21
- {[0-9a-f]} → regex: qualquer char hex
22
- {\d} → regex: qualquer dígito
23
- {[^aeiou]} → regex: consoante
31
+
32
+ ```js
33
+ import maskara from 'maskarajs'
24
34
  ```
25
35
 
26
36
  ## API
27
37
 
28
38
  ```js
29
- import mask from './mask.js'
30
-
31
- mask(pattern, value) // aplica máscara string formatada
32
- mask.raw(pattern, value) // valor limpo / resultado do transform
33
- mask.is(pattern, value) // padrão completo? boolean
34
- mask.hint(pattern) // placeholder string
35
- mask.format(pattern, value) // alias semântico de mask()
36
- mask.rawLength(pattern, value) // chars de input preenchidos → number
37
- mask.patternLength(pattern) // tamanho mascarado completo → number
38
- mask.define(name, definition) // registra máscara nomeada
39
- mask.undefine(name) // remove do registry
40
- mask.names() // lista nomes registrados → string[]
41
- mask.defineSlot(symbol, definition)// cria/sobrescreve token de input
42
- mask.undefineSlot(symbol) // remove token customizado
43
- mask.slots() // lista tokens de input disponíveis
44
- mask.on(input, pattern, options) // vincula a input DOM → cleanup()
45
- mask.create(presets) // instância isolada com registry próprio
39
+ maskara(pattern, value) // apply mask -> formatted string
40
+ maskara.raw(pattern, value) // clean value / transform result
41
+ maskara.is(pattern, value) // complete pattern? -> boolean
42
+ maskara.hint(pattern) // readable placeholder -> string
43
+ maskara.format(pattern, value) // semantic alias for maskara()
44
+ maskara.rawLength(pattern, value) // filled input chars -> number
45
+ maskara.patternLength(pattern) // full formatted length -> number
46
+ maskara.define(name, definition) // register a named mask
47
+ maskara.undefine(name) // remove a named mask
48
+ maskara.names() // list registered names -> string[]
49
+ maskara.defineSlot(symbol, definition) // create or override an input token
50
+ maskara.undefineSlot(symbol) // remove a custom token
51
+ maskara.slots() // list available input tokens
52
+ maskara.on(input, pattern, options) // bind to a DOM input -> cleanup()
53
+ maskara.create(presets) // isolated instance with its own registry
46
54
  ```
47
55
 
48
- ## Exemplos
56
+ ## React adapter
57
+
58
+ The React adapter is optional and lives in `maskarajs/react`. Use `useMaskara` directly for small forms, or wrap your app with `MaskaraProvider` when you have an isolated engine created with `maskara.create()`.
59
+
60
+ ```tsx
61
+ import maskara from 'maskarajs'
62
+ import { MaskaraProvider, useMaskara } from 'maskarajs/react'
63
+
64
+ const appMaskara = maskara.create({
65
+ cpf: { pattern: '###[.]###[.]###[-]##' },
66
+ phone: { pattern: ['[(]##[)] ####[-]####', '[(]##[)] #####[-]####'] },
67
+ })
68
+
69
+ function CPFInput() {
70
+ const cpf = useMaskara('cpf')
71
+
72
+ return (
73
+ <input
74
+ {...cpf.inputProps({ inputMode: 'numeric' })}
75
+ aria-label="CPF"
76
+ />
77
+ )
78
+ }
79
+
80
+ export function App() {
81
+ return (
82
+ <MaskaraProvider engine={appMaskara}>
83
+ <CPFInput />
84
+ </MaskaraProvider>
85
+ )
86
+ }
87
+ ```
88
+
89
+ `useMaskara` still accepts an `engine` option when a single field needs a specific instance:
90
+
91
+ ```tsx
92
+ const cpf = useMaskara('cpf', { engine: appMaskara })
93
+ ```
94
+
95
+ The hook stores field state. The engine stores mask configuration.
96
+
97
+ ## Examples
49
98
 
50
99
  ```js
51
- // Padrão único
52
- mask('###[.]###[.]###[-]##', '12345678909')
53
- // '123.456.789-09'
100
+ // Single pattern
101
+ maskara('###[.]###[.]###[-]##', '12345678909')
102
+ // -> '123.456.789-09'
54
103
 
55
- // Padrão dinâmico (array) escolhe pelo tamanho do input
56
- mask(['[(]##[)] ####[-]####', '[(]##[)] #####[-]####'], '11987654321')
57
- // '(11) 98765-4321'
104
+ // Dynamic pattern array: chooses by input size
105
+ maskara(['[(]##[)] ####[-]####', '[(]##[)] #####[-]####'], '11987654321')
106
+ // -> '(11) 98765-4321'
58
107
 
59
- // Slot com restrição
60
- mask('{4}### #### #### ####', '4111111111111111')
61
- // '4111 1111 1111 1111' (só aceita Visa — começa com 4)
108
+ // Restricted slot
109
+ maskara('{4}### #### #### ####', '4111111111111111')
110
+ // -> '4111 1111 1111 1111'
62
111
 
63
- // Slot com regex
64
- mask('{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}', '1a2b3c')
65
- // '1a2b3c'
112
+ // Regex slot
113
+ maskara('{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}', '1a2b3c')
114
+ // -> '1a2b3c'
66
115
  ```
67
116
 
68
- ### Mais casos comuns
117
+ ### Common cases
69
118
 
70
119
  ```js
71
- // CEP
72
- mask('#####[-]###', '01310930')
73
- // '01310-930'
120
+ // Brazilian ZIP code
121
+ maskara('#####[-]###', '01310930')
122
+ // -> '01310-930'
74
123
 
75
124
  // CNPJ
76
- mask('##[.]###[.]###[/]####[-]##', '11222333000181')
77
- // '11.222.333/0001-81'
125
+ maskara('##[.]###[.]###[/]####[-]##', '11222333000181')
126
+ // -> '11.222.333/0001-81'
78
127
 
79
- // Cartão Visa
80
- mask('{4}### #### #### ####', '5111111111111111')
81
- // '' (primeiro char não passou)
128
+ // Visa card
129
+ maskara('{4}### #### #### ####', '5111111111111111')
130
+ // -> '' (the first char did not pass)
82
131
 
83
- // Hex color com paste sujo
84
- mask('{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}', '1z2b3c')
85
- // '12b3c'
132
+ // Hex color with dirty paste
133
+ maskara('{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}{[0-9a-fA-F]}', '1z2b3c')
134
+ // -> '12b3c'
86
135
  ```
87
136
 
88
- ## mask.define com transform
137
+ ## `maskara.define` with `transform`
89
138
 
90
139
  ```js
91
- mask.define('date', {
140
+ maskara.define('date', {
92
141
  pattern: '##[/]##[/]####',
93
142
  validate: (raw, masked, complete) => {
94
143
  if (raw.length < 4) return true
@@ -102,23 +151,21 @@ mask.define('date', {
102
151
  },
103
152
  })
104
153
 
105
- mask('date', '01012025') // '01/01/2025'
106
- mask.raw('date', '01/01/2025') // Date(2025-01-01)
107
- mask.raw('date', '01/01') // null (incompleto — transform decidiu)
108
- mask.is('date', '01/01/2025') // true
109
- mask.hint('date') // '00/00/0000'
110
- mask.rawLength('date', '01/01') // 4
111
- mask.patternLength('date') // 10
154
+ maskara('date', '01012025') // -> '01/01/2025'
155
+ maskara.raw('date', '01/01/2025') // -> Date(2025-01-01)
156
+ maskara.raw('date', '01/01') // -> null
157
+ maskara.is('date', '01/01/2025') // -> true
158
+ maskara.hint('date') // -> '00/00/0000'
159
+ maskara.rawLength('date', '01/01') // -> 4
160
+ maskara.patternLength('date') // -> 10
112
161
  ```
113
162
 
114
- ## validate validação incremental
163
+ ## `validate`: incremental validation
115
164
 
116
- Use `validate` em máscaras nomeadas quando a regra depende do que foi digitado.
117
- Isso resolve casos onde a sintaxe caractere a caractere não é suficiente, como mês
118
- entre `01` e `12`.
165
+ Use `validate` on named masks when the rule depends on what has already been typed. This covers cases where character-by-character syntax is not enough, such as accepting only months from `01` to `12`.
119
166
 
120
167
  ```js
121
- mask.define('month', {
168
+ maskara.define('month', {
122
169
  pattern: '{0-1}#',
123
170
  validate: (raw, masked, complete) => {
124
171
  if (!complete) return true
@@ -127,98 +174,129 @@ mask.define('month', {
127
174
  },
128
175
  })
129
176
 
130
- mask('month', '12') // '12'
131
- mask('month', '19') // '1' (o 9 é recusado)
132
- mask.is('month', '12') // true
133
- mask.is('month', '19') // false
177
+ maskara('month', '12') // -> '12'
178
+ maskara('month', '19') // -> '1'
179
+ maskara.is('month', '12') // -> true
180
+ maskara.is('month', '19') // -> false
181
+ ```
182
+
183
+ ## Conditional masks
184
+
185
+ Use `patterns` + `select` on a named mask when the mask must change because of the typed value, not only because of length. Arrays of strings keep the old behavior and still choose the smallest pattern that fits the input. Conditional masks live inside `define` or `create`, where product rules already belong.
186
+
187
+ `select(raw, value)` receives the value without literals from all possible patterns and returns a key from `patterns`.
188
+
189
+ ```js
190
+ const cpf = '###[.]###[.]###[-]##'
191
+ const cnpj = '##[.]###[.]###[/]####[-]##'
192
+
193
+ maskara.define('smartDocument', {
194
+ patterns: { cpf, cnpj },
195
+ select: raw => raw.includes('123') ? 'cnpj' : 'cpf',
196
+ })
197
+
198
+ maskara('smartDocument', '98765432100')
199
+ // -> '987.654.321-00'
200
+
201
+ maskara('smartDocument', '12345678000199')
202
+ // -> '12.345.678/0001-99'
134
203
  ```
135
204
 
136
- ## Slots customizados sua própria linguagem de pattern
205
+ It also works with isolated instances:
137
206
 
138
- Além de `#`, `@`, `*` e `{expr}`, você pode criar símbolos que façam sentido
139
- para o seu time. Isso ajuda quando o projeto quer uma linguagem mais expressiva,
140
- mais curta ou alinhada ao domínio do produto.
207
+ ```js
208
+ const forms = maskara.create({
209
+ smartDocument: {
210
+ patterns: { cpf, cnpj },
211
+ select: raw => raw.startsWith('9') ? 'cpf' : 'cnpj',
212
+ },
213
+ })
214
+ ```
215
+
216
+ Prefer conditional masks for rules that depend on the value itself: prefixes, country codes, card BINs, product codes or any domain rule. Prefer a simple string array when the only difference is input size, such as phone numbers with 10 or 11 digits.
217
+
218
+ ## Custom slots: create your own pattern language
219
+
220
+ Besides `#`, `@`, `*` and `{expr}`, you can create symbols that make sense for your team. This is useful when a project needs a shorter, more expressive language aligned with its product domain.
141
221
 
142
222
  ```js
143
- mask.defineSlot('N', {
223
+ maskara.defineSlot('N', {
144
224
  test: ch => /\d/.test(ch),
145
225
  hint: '0',
146
226
  })
147
227
 
148
- mask('NNN[-]NN', '12345') // '123-45'
149
- mask.hint('NNN[-]NN') // '000-00'
150
- mask.slots() // ['#', '@', '*', 'N']
228
+ maskara('NNN[-]NN', '12345') // -> '123-45'
229
+ maskara.hint('NNN[-]NN') // -> '000-00'
230
+ maskara.slots() // -> ['#', '@', '*', 'N']
151
231
  ```
152
232
 
153
- Você também pode passar uma `RegExp` direta ou apenas uma função:
233
+ You can also pass a `RegExp` or a function directly:
154
234
 
155
235
  ```js
156
- mask.defineSlot('H', /[0-9a-f]/i)
157
- mask.defineSlot('V', ch => 'AEIOUaeiou'.includes(ch))
236
+ maskara.defineSlot('H', /[0-9a-f]/i)
237
+ maskara.defineSlot('V', ch => 'AEIOUaeiou'.includes(ch))
158
238
 
159
- mask('HHHHHH', '1a2b3c') // '1a2b3c'
160
- mask('VVV', 'mask') // 'a'
239
+ maskara('HHHHHH', '1a2b3c') // -> '1a2b3c'
240
+ maskara('VVV', 'mask') // -> 'a'
161
241
  ```
162
242
 
163
- O registro global afeta o engine global. Para bibliotecas, design systems ou
164
- produtos com regras próprias, prefira uma instância isolada:
243
+ Global slots affect the global engine. For libraries, design systems or products with their own rules, prefer an isolated instance:
165
244
 
166
245
  ```js
167
- const forge = mask.create()
246
+ const forge = maskara.create()
168
247
 
169
248
  forge.defineSlot('N', { test: ch => /\d/.test(ch), hint: '0' })
170
249
  forge.defineSlot('#', { test: ch => /[1-9]/.test(ch), hint: '1' })
171
250
 
172
- forge('NNN[-]NN', '12345') // '123-45'
173
- forge('#', '0') // '' (# foi sobrescrito nesta instância)
174
- mask('#', '0') // '0' (global continua igual)
251
+ forge('NNN[-]NN', '12345') // -> '123-45'
252
+ forge('#', '0') // -> ''
253
+ maskara('#', '0') // -> '0' (global stays unchanged)
175
254
  ```
176
255
 
177
- Se um símbolo registrado precisar aparecer como texto fixo, escape com
178
- colchetes:
256
+ If a registered symbol must appear as fixed text, escape it with brackets:
179
257
 
180
258
  ```js
181
- mask.defineSlot('N', /\d/)
259
+ maskara.defineSlot('N', /\d/)
182
260
 
183
- mask('[N]##', '45') // 'N45'
184
- mask('N##', '145') // '145'
261
+ maskara('[N]##', '45') // -> 'N45'
262
+ maskara('N##', '145') // -> '145'
185
263
  ```
186
264
 
187
- ## mask.on qualquer framework
265
+ ## `maskara.on`: any framework
188
266
 
189
267
  ```js
190
268
  // Vanilla JS
191
- const off = mask.on(inputEl, 'cpf', {
192
- onValue: raw => setState(raw),
193
- onMasked: masked => setLabel(masked),
269
+ const off = maskara.on(inputEl, 'cpf', {
270
+ onValue: raw => setState(raw),
271
+ onMaskara: masked => setLabel(masked),
194
272
  })
195
- off() // remove listeners
273
+ off()
196
274
 
197
275
  // React
198
276
  useEffect(() => {
199
- return mask.on(ref.current, 'date', {
200
- onValue: (date) => setValue(date), // Date | null
277
+ return maskara.on(ref.current, 'date', {
278
+ onValue: date => setValue(date), // Date | null
201
279
  })
202
280
  }, [])
203
281
 
204
282
  // Vue
205
283
  onMounted(() => {
206
- mask.on(inputRef.value, 'phone', {
207
- onValue: v => emit('update:modelValue', v),
284
+ maskara.on(inputRef.value, 'phone', {
285
+ onValue: value => emit('update:modelValue', value),
208
286
  })
209
287
  })
210
288
 
211
289
  // Svelte action
212
290
  function maskAction(node, pattern) {
213
- const off = mask.on(node, pattern)
291
+ const off = maskara.on(node, pattern)
214
292
  return { destroy: off }
215
293
  }
216
294
  ```
217
295
 
218
- ## mask.create instâncias isoladas
296
+ ## `maskara.create`: isolated instances
219
297
 
220
298
  ```js
221
- export const maskBR = mask.create({
299
+ export const maskaraBR = maskara.create({
222
300
  cpf: { pattern: '###[.]###[.]###[-]##' },
223
301
  cnpj: { pattern: '##[.]###[.]###[/]####[-]##' },
224
302
  phone: { pattern: ['[(]##[)] ####[-]####', '[(]##[)] #####[-]####'] },
@@ -231,53 +309,78 @@ export const maskBR = mask.create({
231
309
  return isNaN(dt) ? null : dt
232
310
  },
233
311
  },
234
- money: {
235
- pattern: '########[,]##',
236
- transform: raw => parseInt(raw || '0', 10) / 100,
237
- },
238
312
  })
239
313
 
240
- export const maskUS = mask.create({
314
+ export const maskaraUS = maskara.create({
241
315
  ssn: { pattern: '###[-]##[-]####' },
242
316
  zip: { pattern: '#####[-]####' },
243
317
  phone: { pattern: '[(]###[)] ###[-]####' },
244
318
  })
245
319
 
246
- // Mesma API, registries isolados — alterações em maskBR não afetam maskUS
247
- maskBR('cpf', '12345678909') // '123.456.789-09'
248
- maskBR.raw('date', '01/01/2025') // Date
249
- maskBR.names() // ['cpf', 'cnpj', 'phone', 'cep', 'date', 'money']
250
- maskUS.names() // → ['ssn', 'zip', 'phone']
320
+ maskaraBR('cpf', '12345678909') // -> '123.456.789-09'
321
+ maskaraBR.raw('date', '01/01/2025') // -> Date
322
+ maskaraBR.names() // -> ['cpf', 'cnpj', 'phone', 'cep', 'date']
323
+ maskaraUS.names() // -> ['ssn', 'zip', 'phone']
251
324
  ```
252
325
 
253
- ## rawLength e patternLength
326
+ ## Official Brazilian presets
327
+
328
+ If you want common Brazilian masks ready to use, import `maskarajs/presets/br` and create an isolated engine from it.
329
+
330
+ ```ts
331
+ import maskara from 'maskarajs'
332
+ import { br, type BrazilPresetRegistry } from 'maskarajs/presets/br'
333
+
334
+ const maskaraBR = maskara.create<BrazilPresetRegistry>(br)
335
+
336
+ maskaraBR('cpf', '12345678909') // -> '123.456.789-09'
337
+ maskaraBR('cnpj', '11222333000181') // -> '11.222.333/0001-81'
338
+ maskaraBR('phone', '11987654321') // -> '(11) 98765-4321'
339
+ maskaraBR.raw('cep', '01310-930') // -> '01310930'
340
+ maskaraBR.raw('date', '01/12/2025') // -> Date
341
+ maskaraBR.raw('money', '1299,90') // -> 1299.9
342
+ ```
343
+
344
+ The preset includes:
345
+
346
+ | Name | Pattern / behavior |
347
+ |---|---|
348
+ | `cpf` | `000.000.000-00` |
349
+ | `cnpj` | `00.000.000/0000-00` |
350
+ | `cep` | `00000-000`, returns `null` until complete |
351
+ | `phone` | landline or mobile phone |
352
+ | `date` | `DD/MM/YYYY`, rejects invalid months and returns `Date \| null` |
353
+ | `month` | accepts only `01` to `12` |
354
+ | `money` | decimal money value from cents |
355
+
356
+ ## `rawLength` and `patternLength`
254
357
 
255
358
  ```js
256
- const filled = mask.rawLength('cpf', value) // chars preenchidos (sem literais)
257
- const total = mask.patternLength('cpf') // tamanho total mascarado
359
+ const filled = maskara.rawLength('cpf', value)
360
+ const total = maskara.patternLength('cpf')
258
361
 
259
- const pct = mask('cpf', value).length / total * 100
260
- const ready = mask.is('cpf', value) // habilita submit
261
- const label = `${filled} raw chars` // "7 raw chars"
362
+ const pct = maskara('cpf', value).length / total * 100
363
+ const ready = maskara.is('cpf', value)
364
+ const label = `${filled} raw chars`
262
365
  ```
263
366
 
264
- `patternLength` conta o tamanho final mascarado, incluindo literais. Exemplos:
367
+ `patternLength` counts the final formatted length, including literals:
265
368
 
266
369
  ```js
267
- mask.patternLength('##[/]##[/]####') // 10
268
- mask.patternLength('###[.]###[.]###[-]##') // 14
269
- mask.patternLength('{4}### #### #### ####') // 19
370
+ maskara.patternLength('##[/]##[/]####') // -> 10
371
+ maskara.patternLength('###[.]###[.]###[-]##') // -> 14
372
+ maskara.patternLength('{4}### #### #### ####') // -> 19
270
373
  ```
271
374
 
272
- ## Showcase visual
375
+ ## Visual showcase
273
376
 
274
- O projeto `maskforge-showcase` demonstra a lib com:
377
+ The `maskforge-showcase` project demonstrates the library with:
275
378
 
276
- - playground destacado com máscara aplicada enquanto o usuário digita;
277
- - visualização do pattern em blocos: slot, literal e expressão;
278
- - exemplos para React, Vue e Vanilla;
279
- - receitas interativas para `validate`, `define` e `create`;
280
- - análise de benchmark local.
379
+ - a playground that applies the mask while the user types;
380
+ - a visual pattern map with slot, literal and expression blocks;
381
+ - examples for React, Vue and Vanilla JavaScript;
382
+ - interactive recipes for `validate`, `define`, `defineSlot` and `create`;
383
+ - local benchmark notes.
281
384
 
282
385
  ```bash
283
386
  cd maskforge-showcase
@@ -285,54 +388,121 @@ npm install
285
388
  npm run dev
286
389
  ```
287
390
 
288
- ## Benchmark local
391
+ ## Local benchmark
289
392
 
290
- Benchmark simples rodado em Node/WSL com 200.000 iterações por caso após warmup.
291
- Os valores variam por máquina, mas ajudam a orientar expectativas de uso em input.
393
+ Simple benchmark executed in Node/WSL with 200,000 iterations per case after warmup. Numbers vary by machine, but they help set expectations for input-level usage.
292
394
 
293
- | Caso | Resultado |
395
+ | Case | Result |
294
396
  |---|---:|
295
- | CPF format | 39.705 ops/s |
296
- | Phone dynamic | 32.455 ops/s |
297
- | Date validate | 64.572 ops/s |
298
- | Raw extraction | 44.729 ops/s |
397
+ | CPF format | 39,705 ops/s |
398
+ | Phone dynamic | 32,455 ops/s |
399
+ | Date validate | 64,572 ops/s |
400
+ | Raw extraction | 44,729 ops/s |
299
401
 
300
402
  ```js
301
403
  const iterations = 200000
302
404
  for (let i = 0; i < iterations; i++) {
303
- mask('###[.]###[.]###[-]##', '12345678909')
405
+ maskara('###[.]###[.]###[-]##', '12345678909')
304
406
  }
305
407
  ```
306
408
 
307
- ## transform — contrato
409
+ ## `transform` contract
308
410
 
309
411
  ```js
310
412
  // transform(raw, masked, complete) => T
311
413
  // validate(raw, masked, complete) => boolean
312
414
  //
313
- // raw string com os chars de input sem literais
314
- // masked string formatada com a máscara
315
- // complete true quando todos os slots estão preenchidos
415
+ // raw -> input chars without literals
416
+ // masked -> formatted string
417
+ // complete -> true when every slot is filled
316
418
  //
317
- // Sem transform mask.raw() retorna string crua, sempre
318
- // Com transform → mask.raw() retorna o que transform devolver, sempre
319
- // O transform decide o que fazer com input parcial
320
-
321
- mask.define('money', {
322
- pattern: '########[,]##',
323
- transform: raw => parseInt(raw || '0', 10) / 100,
324
- // retorna number sempre — mesmo parcial
325
- })
419
+ // Without transform -> maskara.raw() always returns the raw string
420
+ // With transform -> maskara.raw() always returns whatever transform returns
421
+ ```
326
422
 
327
- mask.define('date', {
328
- pattern: '##[/]##[/]####',
329
- transform: (raw, masked, complete) => {
330
- if (!complete) return null // null enquanto incompleto
331
- return new Date(...) // Date quando completo
332
- },
423
+ ## License
424
+
425
+ MIT
426
+
427
+ ## React hook
428
+
429
+ `maskarajs/react` exports a small `useMaskara` hook. It lives in a separate entrypoint, so the core package stays framework-agnostic for users who do not use React.
430
+
431
+ ```tsx
432
+ import { useMaskara } from 'maskarajs/react'
433
+
434
+ export function CPFField() {
435
+ const cpf = useMaskara('###[.]###[.]###[-]##')
436
+
437
+ return <input {...cpf.inputProps({ inputMode: 'numeric' })} />
438
+ }
439
+ ```
440
+
441
+ ### React Hook Form
442
+
443
+ Use `onValue` to send the raw value to React Hook Form while the input keeps the masked value on screen.
444
+
445
+ ```tsx
446
+ import { Controller, useForm } from 'react-hook-form'
447
+ import { useMaskara } from 'maskarajs/react'
448
+
449
+ type FormValues = {
450
+ cpf: string
451
+ }
452
+
453
+ function CPFController({ field }) {
454
+ const cpf = useMaskara('###[.]###[.]###[-]##', {
455
+ value: field.value,
456
+ onValue: field.onChange,
457
+ })
458
+
459
+ return (
460
+ <input
461
+ {...cpf.inputProps({
462
+ name: field.name,
463
+ onBlur: field.onBlur,
464
+ ref: field.ref,
465
+ inputMode: 'numeric',
466
+ })}
467
+ />
468
+ )
469
+ }
470
+
471
+ export function Form() {
472
+ const { control, handleSubmit } = useForm<FormValues>({
473
+ defaultValues: { cpf: '' },
474
+ })
475
+
476
+ return (
477
+ <form onSubmit={handleSubmit(console.log)}>
478
+ <Controller
479
+ name="cpf"
480
+ control={control}
481
+ render={({ field }) => <CPFController field={field} />}
482
+ />
483
+ </form>
484
+ )
485
+ }
486
+ ```
487
+
488
+ ### Zod
489
+
490
+ Keep the form value raw and validate it normally.
491
+
492
+ ```ts
493
+ import { z } from 'zod'
494
+
495
+ export const schema = z.object({
496
+ cpf: z.string().length(11, 'CPF must contain 11 digits'),
333
497
  })
334
498
  ```
335
499
 
336
- ## Licença
500
+ ### Yup
337
501
 
338
- MIT
502
+ ```ts
503
+ import * as yup from 'yup'
504
+
505
+ export const schema = yup.object({
506
+ cpf: yup.string().length(11, 'CPF must contain 11 digits').required(),
507
+ })
508
+ ```