Skip to content

Commit a4089eb

Browse files
authored
feat: add support optional characters
Implements optional character in the mask by use of '?' similar to how it is done in regex. Implements #23 BREAKING CHANGE: The "?" is now a special character, if you are using this character as part of your mask you must now escape it, or change the token used for optional characters in your token definition.
1 parent 39b743f commit a4089eb

File tree

5 files changed

+62
-2
lines changed

5 files changed

+62
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export default {
6363
+ `A` = alpha characters, transformed to uppercase
6464
+ `a` = alpha characters, transformed to lowercase
6565
+ `\` = escape any of the above characters
66+
+ `?` = mark the preceding character as optional
6667

6768
See the [token source file](https://github.com/RonaldJerez/vue-input-facade/blob/master/src/tokens.js) for definition signature
6869

docs/component.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ let masked = true
2424
<display :value="franceIBAN" />
2525
```
2626

27+
### Optional next mask
28+
29+
```js
30+
let value = '192.168.10.1'
31+
let masked = false
32+
33+
<example label="IP address">
34+
<input-facade mask="##?#?.##?#?.##?#?.##?#?" v-model="value" :masked="masked" />
35+
</example>
36+
37+
<checkbox v-model="masked" />
38+
<display :value="value" />
39+
```
40+
2741
### Dynamic Masks
2842

2943
Accepts an array of masking pattern and dynamically chooses the appropriate one based on the number of characters in the field.

src/masker.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export function formatter(value, config) {
6767

6868
let output = new FacadeValue()
6969
let escaped = false
70+
let optional = false
7071

7172
let valueIndex = 0
7273
let maskIndex = 0
@@ -75,6 +76,8 @@ export function formatter(value, config) {
7576
while (maskIndex < mask.length) {
7677
const maskChar = mask[maskIndex]
7778
const masker = tokens[maskChar]
79+
const nextMaskChar = mask[maskIndex + 1]
80+
const nextMasker = tokens[nextMaskChar]
7881
let char = value[valueIndex]
7982

8083
// no more input characters and next character is a masked one
@@ -88,14 +91,26 @@ export function formatter(value, config) {
8891
continue
8992
}
9093

91-
if (masker.pattern.test(char)) {
94+
if (masker.optional || (nextMasker && nextMasker.optional)) {
95+
optional = true
96+
}
97+
98+
if (masker.pattern && masker.pattern.test(char)) {
9299
char = masker.transform ? masker.transform(char) : char
93100
output.unmasked += char
94101
output.masked += accumulator + char
95102

96103
accumulator = ''
97104
maskIndex++
105+
optional = false
98106
}
107+
108+
if (optional) {
109+
optional = false
110+
maskIndex++
111+
continue
112+
}
113+
99114
valueIndex++
100115
} else {
101116
accumulator += maskChar
@@ -109,6 +124,7 @@ export function formatter(value, config) {
109124
}
110125
}
111126

127+
optional = false
112128
escaped = false
113129
maskIndex++
114130
}

src/tokens.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ export default {
44
S: { pattern: /[a-z]/i },
55
A: { pattern: /[a-z]/i, transform: (v) => v.toLocaleUpperCase() },
66
a: { pattern: /[a-z]/i, transform: (v) => v.toLocaleLowerCase() },
7-
'\\': { escape: true }
7+
'\\': { escape: true },
8+
'?': { optional: true }
89
}

tests/formatter.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,34 @@ test('empty -> +1 # 5', () => {
6666
expect(formatter('', { mask: '+1 # 5', prefill: true })).toMatchObject({ masked: '+1 ', unmasked: '' })
6767
})
6868

69+
test('12.345 -> ##?#?.###', () => {
70+
expect(formatter('12.345', { mask: '##?#?.###' })).toMatchObject({ masked: '12.345', unmasked: '12345' })
71+
})
72+
73+
test('12.3 -> ##?#?.##?#?', () => {
74+
expect(formatter('12.3', { mask: '##?#?.##?#?' })).toMatchObject({ masked: '12.3', unmasked: '123' })
75+
})
76+
77+
test('123.456 -> ##?#?.##?#?', () => {
78+
expect(formatter('123.456', { mask: '##?#?.##?#?' })).toMatchObject({ masked: '123.456', unmasked: '123456' })
79+
})
80+
81+
test('21986892332 -> (0##?) #####-####', () => {
82+
expect(formatter('21986892332', { mask: '(0##?) #####-####' })).toMatchObject({ masked: '(021) 98689-2332', unmasked: '21986892332' })
83+
})
84+
85+
test('2)986892332 -> (0##?) #####-####', () => {
86+
expect(formatter('2)986892332', { mask: '(0##?) #####-####' })).toMatchObject({ masked: '(02) 98689-2332', unmasked: '2986892332' })
87+
})
88+
89+
test('12.3456abc -> ##?#?.###.#A?a?S', () => {
90+
expect(formatter('12.3456abc', { mask: '##?#?.###.#A?a?S' })).toMatchObject({ masked: '12.345.6Abc', unmasked: '123456Abc' })
91+
})
92+
93+
test('12.3456ac -> ##?#?.###.##?a?S', () => {
94+
expect(formatter('12.3456ac', { mask: '##?#?.###.##?a?S' })).toMatchObject({ masked: '12.345.6ac', unmasked: '123456ac' })
95+
})
96+
6997
test('France IBAN', () => {
7098
expect(
7199
formatter('FR7630006000011234567890189', {

0 commit comments

Comments
 (0)