Last active
September 3, 2021 06:59
-
-
Save sertraline/709e39d7038a85e9e3e6bca1f8ca2a0d to your computer and use it in GitHub Desktop.
Vue+Vuetify phone mask w/o dependencies
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<div> | |
<v-text-field | |
:ref="`input-mask-${identifier}`" | |
v-model="inputPhone" | |
outlined | |
dense | |
label="Номер телефона" | |
hide-details | |
@keydown="input" | |
@focusin="focused" | |
></v-text-field> | |
</div> | |
</template> | |
<script> | |
export default { | |
name: 'PhoneMask', | |
props: { | |
code: { type: String, required: true }, | |
setPhone: { type: Function, required: true }, | |
propMask: { type: String, required: true }, | |
placeholder: { type: String, required: true } | |
}, | |
data() { | |
return { | |
identifier: this.uuidV4(), | |
handler: {}, | |
inputPhone: '', | |
mask: '', | |
start: 0 | |
}; | |
}, | |
watch: { | |
code(el) { | |
this.mask = el + this.propMask; | |
this.setLength(); | |
this.setValue(); | |
this.start = this.placeHolderPosition() - 1; | |
}, | |
inputPhone(el) { | |
this.setPhone(el); | |
} | |
}, | |
mounted() { | |
this.handler = this.$refs[ | |
`input-mask-${this.identifier}` | |
].$el.querySelector('input'); | |
this.mask = this.code + this.propMask; | |
this.setLength(); | |
this.setValue(); | |
this.start = this.placeHolderPosition() - 1; | |
}, | |
methods: { | |
uuidV4() { | |
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => | |
( | |
c ^ | |
(crypto.getRandomValues(new Uint8Array(1))[0] & | |
(15 >> (c / 4))) | |
).toString(16) | |
); | |
}, | |
focused() { | |
let a = this.placeHolderPosition(); | |
(this.handler.selectionStart = a), (this.handler.selectionEnd = a); | |
}, | |
input(a) { | |
if ( | |
(this.isDirectionKey(a.key) || a.preventDefault(), | |
this.isNum(a.key)) | |
) | |
this.changeChar(a.key); | |
else if (this.isDeletionKey(a.key)) | |
if ('Backspace' === a.key) { | |
let text = this.getSelectionText(); | |
if (text.length === 0) { | |
this.changeChar(this.placeholder, -1, this.start); | |
} else { | |
this.clear(this.getSelectionText()); | |
} | |
} else this.changeChar(this.placeholder); | |
}, | |
getSelectionText() { | |
let text = ''; | |
if (window.getSelection) { | |
text = window.getSelection().toString(); | |
} else if ( | |
document.selection && | |
document.selection.type !== 'Control' | |
) { | |
text = document.selection.createRange().text; | |
} | |
return text; | |
}, | |
setLength() { | |
this.handler.maxLength = this.mask.length; | |
}, | |
setValue() { | |
this.inputPhone = this.mask; | |
this.handler.value = this.inputPhone; | |
}, | |
isNum(a) { | |
return ( | |
!isNaN(a) && | |
parseInt(+a) === parseInt(a) && | |
!isNaN(parseInt(a, 10)) | |
); | |
}, | |
isDeletionKey(a) { | |
return 'Delete' === a || 'Backspace' === a; | |
}, | |
isDirectionKey(a) { | |
return ( | |
'ArrowUp' === a || | |
'ArrowDown' === a || | |
'ArrowRight' === a || | |
'ArrowLeft' === a || | |
'Tab' === a | |
); | |
}, | |
isPlaceholder(a) { | |
return a === this.placeholder; | |
}, | |
placeHolderPosition() { | |
return this.inputPhone.indexOf(this.placeholder); | |
}, | |
clear(text) { | |
let start = this.handler.selectionStart - this.code.length; | |
let value = this.inputPhone; | |
value = value.slice(this.code.length); | |
let start_string = value.substring(0, start); | |
let replace_string = value.substring(start, start + text.length); | |
let end_string = value.substring(start + text.length, value.length); | |
replace_string = replace_string.split(''); | |
for (let i = 0; i < replace_string.length; i++) { | |
if (!isNaN(replace_string[i])) { | |
replace_string[i] = this.placeholder; | |
} | |
} | |
this.inputPhone = | |
this.code + start_string + replace_string.join('') + end_string; | |
this.handler.value = this.inputPhone; | |
this.handler.selectionStart = start + this.code.length; | |
this.handler.selectionEnd = start + this.code.length; | |
if (this.handler.selectionEnd < 1) { | |
this.handler.selectionStart = this.code.length + 1; | |
this.handler.selectionEnd = this.code.length + 1; | |
} | |
}, | |
changeChar(a, b = 1, len = this.mask.length) { | |
let value = this.inputPhone; | |
let sel_start = | |
0 < b | |
? this.handler.selectionStart | |
: this.handler.selectionStart - 1; | |
let g = ''; | |
if (sel_start === len) return !1; | |
if (sel_start <= this.code.length - 1) return !1; | |
if ( | |
!this.isNum(value[sel_start]) && | |
!this.isPlaceholder(value[sel_start]) | |
) { | |
do { | |
if (((sel_start += b), sel_start === len)) { | |
return !1; | |
} | |
} while ( | |
!this.isNum(value[sel_start]) && | |
!this.isPlaceholder(value[sel_start]) | |
); | |
} | |
g = this.replaceAt(value, sel_start, a); | |
this.inputPhone = g; | |
this.handler.value = this.inputPhone; | |
0 < b && (sel_start += b); | |
this.handler.selectionStart = sel_start; | |
this.handler.selectionEnd = sel_start; | |
}, | |
replaceAt(a, b, c) { | |
return a.substring(0, b) + c + a.substring(++b); | |
} | |
} | |
}; | |
</script> | |
<style scoped></style> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<v-row class="pa-2"> | |
<v-col cols="12" sm="3" md="3" lg="3" class="pr-0"> | |
<v-select | |
v-model="primaryPhoneCode" | |
outlined | |
dense | |
:items="codes" | |
hide-details | |
> | |
<template #selection="data"> | |
{{ data.item.text }} | |
</template> | |
<template #item="data"> | |
{{ data.item.text }} | |
</template> | |
</v-select> | |
</v-col> | |
<v-col cols="12" sm="9" md="9" lg="9"> | |
<phone-mask | |
:code="primaryPhoneCode" | |
:set-phone="setPrimaryPhone" | |
prop-mask="(___)___-__-__" | |
placeholder="_" | |
/> | |
</v-col> | |
</v-row> | |
</template> | |
<script> | |
import PhoneMask from './PhoneMask'; | |
export default { | |
name: 'ParentComponent', | |
components: { PhoneMask }, | |
data() { | |
return { | |
primaryPhone: '', | |
primaryPhoneCode: '+7', | |
codes: [ | |
{ value: '+7', text: '🇷🇺 +7' }, | |
{ value: '+38', text: '🇺🇦 +38' } | |
] | |
} | |
}, | |
methods: { | |
setPrimaryPhone(el) { | |
this.primaryPhone = el; | |
} | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment