Skip to content

Commit ac14173

Browse files
committed
feat(directive): expose the unmasked value in the event so it could be used in the directive
The directive now adds `unmaskedValue` to event.target. Now the unmasked value can be used even if you cannot used the component.
1 parent ff3a10b commit ac14173

File tree

8 files changed

+94
-74
lines changed

8 files changed

+94
-74
lines changed

__tests__/dynamic.test.js

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,48 @@ import {dynamic} from '../src/masker'
22

33
test ('empty', () => {
44
var masks = []
5-
expect(dynamic('12345', { masks })).toBe('')
5+
expect(dynamic('12345', { masks })).toMatchObject({ masked: '', raw: ''})
66
})
77

88
test ('single', () => {
99
var masks = ['#.#']
10-
expect(dynamic('12', { masks })).toBe('1.2')
11-
expect(dynamic('321', { masks })).toBe('3.2')
10+
expect(dynamic('12', { masks })).toMatchObject({ masked: '1.2', raw: '12'})
11+
expect(dynamic('321', { masks })).toMatchObject({ masked: '3.2', raw: '32'})
1212
})
1313

1414
test ('CEP USA/BR', () => {
1515
var masks = ['#####', '#####-###']
16-
expect(dynamic('12345', { masks })).toBe('12345')
17-
expect(dynamic('123456', { masks })).toBe('12345-6')
18-
expect(dynamic('12345678', { masks })).toBe('12345-678')
16+
expect(dynamic('12345', { masks })).toMatchObject({ masked: '12345', raw: '12345'})
17+
expect(dynamic('123456', { masks })).toMatchObject({ masked: '12345-6', raw: '123456'})
18+
expect(dynamic('12345678', { masks })).toMatchObject({ masked: '12345-678', raw: '12345678'})
1919
})
2020

2121
test ('Reverse CEP USA/BR', () => {
2222
var masks = ['#####-###', '#####']
23-
expect(dynamic('12345', { masks })).toBe('12345')
24-
expect(dynamic('123456', { masks })).toBe('12345-6')
25-
expect(dynamic('12345678', { masks })).toBe('12345-678')
23+
expect(dynamic('12345', { masks })).toMatchObject({ masked: '12345', raw: '12345'})
24+
expect(dynamic('123456', { masks })).toMatchObject({ masked: '12345-6', raw: '123456'})
25+
expect(dynamic('12345678', { masks })).toMatchObject({ masked: '12345-678', raw: '12345678'})
2626
})
2727

2828
test ('cpf/cnpj', () => {
2929
var masks = ['###.###.###-##', '##.###.###/####-##']
30-
expect(dynamic('12345678901', { masks })).toBe('123.456.789-01')
31-
expect(dynamic('123456789012', { masks })).toBe('12.345.678/9012-')
30+
expect(dynamic('12345678901', { masks })).toMatchObject({ masked: '123.456.789-01', raw: '12345678901'})
31+
expect(dynamic('123456789012', { masks })).toMatchObject({ masked: '12.345.678/9012-', raw: '123456789012'})
3232
})
3333

3434
test ('bank agency', () => {
3535
var masks = ['####', '####-#', '####-##']
36-
expect(dynamic('1234', { masks })).toBe('1234')
37-
expect(dynamic('12345', { masks })).toBe('1234-5')
38-
expect(dynamic('123456', { masks })).toBe('1234-56')
36+
expect(dynamic('1234', { masks })).toMatchObject({ masked: '1234', raw: '1234'})
37+
expect(dynamic('12345', { masks })).toMatchObject({ masked: '1234-5', raw: '12345'})
38+
expect(dynamic('123456', { masks })).toMatchObject({ masked: '1234-56', raw: '123456'})
3939
})
4040

4141
test ('bank account', () => {
4242
// 12345 123456 1234567 12345678 123456789
4343
var masks = ['#####-#', '######-#', '#######-#', '########-#', '#########-#']
44-
expect(dynamic('123456', { masks })).toBe('12345-6')
45-
expect(dynamic('1234567', { masks })).toBe('123456-7')
46-
expect(dynamic('12345678', { masks })).toBe('1234567-8')
47-
expect(dynamic('123456789', { masks })).toBe('12345678-9')
48-
expect(dynamic('1234567890', { masks })).toBe('123456789-0')
44+
expect(dynamic('123456', { masks })).toMatchObject({ masked: '12345-6', raw: '123456'})
45+
expect(dynamic('1234567', { masks })).toMatchObject({ masked: '123456-7', raw: '1234567'})
46+
expect(dynamic('12345678', { masks })).toMatchObject({ masked: '1234567-8', raw: '12345678'})
47+
expect(dynamic('123456789', { masks })).toMatchObject({ masked: '12345678-9', raw: '123456789'})
48+
expect(dynamic('1234567890', { masks })).toMatchObject({ masked: '123456789-0', raw: '1234567890'})
4949
})

__tests__/formatter.test.js

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,61 @@
11
import {formatter} from '../src/masker'
22

33
test('default vals', () => {
4-
expect(formatter('123')).toBe('')
4+
expect(formatter('123')).toMatchObject({ masked: '', raw: '' })
55
})
66

77
test('12 -> #.#', () => {
8-
expect(formatter('12', { mask: '#.#' })).toBe('1.2')
8+
expect(formatter('12', { mask: '#.#' })).toMatchObject({ masked:'1.2', raw: '12' })
99
})
1010

1111
test('1 -> (#)', () => { // placeholder at the end
12-
expect(formatter('1', { mask: '(#)' })).toBe('(1)')
12+
expect(formatter('1', { mask: '(#)' })).toMatchObject({masked: '(1)', raw: '1'})
1313
})
1414

1515
test('1 -> [(#)]', () => { // two placeholder at the end
16-
expect(formatter('1', { mask: '[(#)]' })).toBe('[(1)]')
16+
expect(formatter('1', { mask: '[(#)]' })).toMatchObject({masked: '[(1)]', raw: '1'})
1717
})
1818

1919
test('1 -> #..#', () => {
20-
expect(formatter('1', { mask: '#..#', short: true })).toBe('1')
20+
expect(formatter('1', { mask: '#..#', short: true })).toMatchObject({ masked: '1', raw: '1' })
2121
})
2222

2323
test('1 -> #.#', () => {
24-
expect(formatter('1', { mask: '#.#' })).toBe('1.')
24+
expect(formatter('1', { mask: '#.#' })).toMatchObject({ masked: '1.', raw: '1' })
2525
})
2626

2727
test('1. -> #.#', () => {
28-
expect(formatter('1.', { mask: '#.#' })).toBe('1.')
28+
expect(formatter('1.', { mask: '#.#' })).toMatchObject({ masked: '1.', raw: '1' })
2929
})
3030

3131
test('123 -> #.#', () => {
32-
expect(formatter('123', { mask: '#.#' })).toBe('1.2')
33-
})
34-
35-
test('raw phone number', () => {
36-
expect(formatter('44998765432', { mask: '+55 (##) #####-####', masked: false })).toBe('44998765432')
32+
expect(formatter('123', { mask: '#.#' })).toMatchObject({ masked: '1.2', raw: '12' })
3733
})
3834

3935
test('abc1234567 -> AAa-####', () => {
40-
expect(formatter('abc1234567', { mask: 'AAa-####' })).toBe('ABc-1234')
36+
expect(formatter('abc1234567', { mask: 'AAa-####' })).toMatchObject({ masked: 'ABc-1234', raw: 'ABc1234' })
4137
})
4238

4339
test('a5-12-34 -> (XX) - ## - ##', () => {
44-
expect(formatter('a5-12-34', { mask: '(XX) - ## - ##' })).toBe('(a5) - 12 - 34')
40+
expect(formatter('a5-12-34', { mask: '(XX) - ## - ##' })).toMatchObject({ masked: '(a5) - 12 - 34', raw: 'a51234' })
4541
})
4642

4743
test('123 -> ##(#)', () => {
48-
expect(formatter('123', { mask: '##(#)' })).toBe('12(3)')
44+
expect(formatter('123', { mask: '##(#)' })).toMatchObject({ masked: '12(3)', raw: '123' })
4945
})
5046

51-
test('123 -> #!#(#)', () => {
52-
expect(formatter('12', { mask: '#!#(#)' })).toBe('1#(2)')
47+
test('123 -> #\#(#)', () => {
48+
expect(formatter('12', { mask: '#\\#(#)' })).toMatchObject({ masked: '1#(2)', raw: '12' })
5349
})
5450

5551
test('12 -> +1 #', () => {
56-
expect(formatter('12', { mask: '+1 #' })).toBe('+1 2')
52+
expect(formatter('12', { mask: '+1 #' })).toMatchObject({ masked: '+1 2', raw: '2' })
5753
})
5854

5955
test('2 -> +1 #', () => {
60-
expect(formatter('2', { mask: '+1 #' })).toBe('+1 2')
56+
expect(formatter('2', { mask: '+1 #' })).toMatchObject({ masked: '+1 2', raw: '2' })
6157
})
6258

6359
test('2 -> +1 # 5', () => {
64-
expect(formatter('2', { mask: '+1 # 5' })).toBe('+1 2 5')
60+
expect(formatter('2', { mask: '+1 # 5' })).toMatchObject({ masked: '+1 2 5', raw: '2' })
6561
})

__tests__/masker.test.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
1-
import masker from '../src/masker'
1+
import masker, { setTokens } from '../src/masker'
22

33
test('no mask given', () => {
4-
expect(masker('123')).toBe('123')
4+
expect(masker('123')).toMatchObject({masked: '123', raw: '123'})
55
})
66

77
test('single mask given', () => {
8-
expect(masker('12', { mask: '#.#' })).toBe('1.2')
8+
expect(masker('12', { mask: '#.#' })).toMatchObject({masked: '1.2', raw: '12' })
99
})
1010

1111
test('multiple mask given', () => {
1212
const config = { mask: ['#.#', '##.#'] }
13-
expect(masker('12', config)).toBe('1.2')
14-
expect(masker('123', config)).toBe('12.3')
13+
expect(masker('12', config)).toMatchObject({ masked: '1.2', raw: '12' })
14+
expect(masker('123', config)).toMatchObject({ masked: '12.3', raw: '123' })
15+
})
16+
17+
test('should override default tokens', () => {
18+
const tokens = {
19+
'F': { pattern: /[a-f0-9]/i },
20+
}
21+
setTokens(tokens)
22+
expect(masker('1DFS', { mask: '#FFFF'})).toMatchObject({ masked: '#1DF', raw: '1DF' })
1523
})

src/component.vue

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
<script>
66
import directive from './directive'
7-
import masker from './masker'
87
98
export default {
109
name: 'InputFacade',
@@ -24,7 +23,8 @@ export default {
2423
data() {
2524
return {
2625
maskedValue: this.value,
27-
lastValue: this.value
26+
lastValue: this.value,
27+
unmaskedValue: ''
2828
}
2929
},
3030
watch: {
@@ -56,14 +56,10 @@ export default {
5656
refresh(event) {
5757
this.maskedValue = event ? event.target.value : this.maskedValue
5858
let emittedValue = this.maskedValue
59+
this.unmaskedValue = event ? event.target.unmaskedValue : this.unmaskedValue || emittedValue
5960
6061
if (this.mask && !this.masked) {
61-
const maskerConfig = {
62-
mask: this.mask,
63-
tokens: this.tokens,
64-
masked: false
65-
}
66-
emittedValue = masker(this.maskedValue, maskerConfig)
62+
emittedValue = this.unmaskedValue
6763
}
6864
6965
// avoid unecessary emit when has no change

src/core.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import masker from './masker'
2-
32
export const CONFIG_KEY = '__input-facade__'
43

4+
export function FacadeValue(val = '') {
5+
this.masked = this.raw = val
6+
}
7+
58
export function trigger(name) {
69
return new Event(name, { bubbles: true, cancelable: true })
710
}
@@ -96,7 +99,10 @@ export function updateValue(el, { emit = true, force = false } = {}) {
9699

97100
if (force || oldValue !== el.value) {
98101
const newValue = masker(el.value, config)
99-
el[CONFIG_KEY].oldValue = el.value = newValue
102+
103+
el[CONFIG_KEY].oldValue = newValue.masked
104+
el.value = newValue.masked
105+
el.unmaskedValue = newValue.raw
100106
emit && el.dispatchEvent(trigger('input'))
101107
}
102108
}

src/docs/App.vue

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,10 @@ hexTokens: {
163163
<label>The Mask:</label>
164164
<input type="text" v-model="directiveMask" />
165165
</div>
166-
<div class="field" v-facade="directiveMask">
166+
<div class="field" v-facade="directiveMask" @input="getDirectiveRaw">
167167
<label>The Input:</label>
168168
<input type="tel" />
169+
Unmasked Value from Directive: {{ directiveRaw }}
169170
</div>
170171
<pre>{{ directive }}</pre>
171172

@@ -218,7 +219,8 @@ export default {
218219
value: '12TgB',
219220
directiveMask: '##/##/####',
220221
model1: '',
221-
model2: ''
222+
model2: '',
223+
directiveRaw: ''
222224
}
223225
},
224226
computed: {
@@ -230,8 +232,11 @@ export default {
230232
}
231233
},
232234
methods: {
233-
updateModel2({ target }) {
234-
this.model2 = target.value
235+
getDirectiveRaw({ target }) {
236+
this.directiveRaw = target.unmaskedValue
237+
},
238+
updateModel2(event) {
239+
this.model2 = event.target.value
235240
}
236241
}
237242
}

src/masker.js

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { normalizeConfig } from './core'
1+
import { normalizeConfig, FacadeValue } from './core'
22
import defaultTokens from './tokens'
33

44
let tokenDefinitions = defaultTokens
@@ -11,39 +11,39 @@ export function dynamic(value, config = {}) {
1111
const masks = config.masks.slice().sort((a, b) => a.length - b.length)
1212
const withConfig = (overrides) => Object.assign({}, config, overrides)
1313

14-
const nextMaskIsLarger = (currentMask, nextMask) => {
15-
return formatter(value, withConfig({ mask: nextMask, short: true })).length > currentMask.length
14+
const nextFacadeIsLarger = (currentMask, nextMask) => {
15+
const nextMaskedVal = formatter(value, withConfig({ mask: nextMask, short: true }))
16+
return nextMaskedVal.masked.length > currentMask.length
1617
}
1718

1819
for (let i = 0; i < masks.length; i++) {
1920
const currentMask = masks[i]
2021
const nextMask = masks[i + 1]
2122

22-
if (!nextMask || !nextMaskIsLarger(currentMask, nextMask)) {
23+
if (!nextMask || !nextFacadeIsLarger(currentMask, nextMask)) {
2324
return formatter(value, withConfig({ mask: currentMask }))
2425
}
2526
}
2627

27-
return '' // empty masks
28+
return new FacadeValue() // empty masks
2829
}
2930

3031
export function formatter(value = '', config = {}) {
31-
const { mask = '', masked = true, tokens = tokenDefinitions, short = false } = config
32+
const { mask = '', tokens = tokenDefinitions, short = false } = config
3233

3334
// ensure we have a string
3435
value = value.toString()
3536

36-
let output = ''
37+
let output = new FacadeValue()
3738
let escaped = false
38-
let userInput = false
3939

4040
let valueIndex = 0
4141
let maskIndex = 0
4242

4343
while (maskIndex < mask.length) {
4444
const maskChar = mask[maskIndex]
4545
const masker = tokens[maskChar]
46-
const char = value[valueIndex]
46+
let char = value[valueIndex]
4747

4848
// no more input charactors and next charactor is a masked char
4949
if (!char && (short || masker)) break
@@ -57,21 +57,26 @@ export function formatter(value = '', config = {}) {
5757
}
5858

5959
if (masker.pattern.test(char)) {
60-
userInput = true
61-
output += masker.transform ? masker.transform(char) : char
60+
char = masker.transform ? masker.transform(char) : char
61+
output.raw += char
62+
output.masked += char
6263
maskIndex++
6364
}
6465
valueIndex++
6566
} else {
66-
if (masked) output += maskChar
67+
output.masked += maskChar
6768
if (char === maskChar) valueIndex++ // user typed the same char
6869

6970
escaped = false
7071
maskIndex++
7172
}
7273
}
7374

74-
return userInput ? output : ''
75+
// if there is no raw value, set masked to empty to avoid
76+
// showing masking characters in an otherwise empty input
77+
if (!output.raw) output.masked = ''
78+
79+
return output
7580
}
7681

7782
// Facade to formatter/dynamic when mask is String or Array
@@ -80,7 +85,7 @@ export default function masker(value, config) {
8085

8186
// disable on empty mask
8287
if (!config.mask) {
83-
return value
88+
return new FacadeValue(value)
8489
}
8590

8691
return Array.isArray(config.mask)

src/plugin.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ function install(Vue, options = {}) {
1818

1919
Vue.component(InputFacade.name, InputFacade)
2020
Vue.directive(options.name || 'facade', facade)
21-
Vue.filter(options.name || 'facade', masker)
21+
Vue.filter(options.name || 'facade', filter)
22+
}
23+
24+
function filter(value, config) {
25+
return masker(value, config).masked
2226
}
2327

2428
export default install
25-
export { InputFacade, facade, tokens, masker }
29+
export { InputFacade, facade, tokens, masker, filter }
2630

2731
// Install by default if included from script tag
2832
if (typeof window !== 'undefined' && window.Vue) {

0 commit comments

Comments
 (0)