Skip to content

Instantly share code, notes, and snippets.

@sertraline
Last active September 3, 2021 06:59
Show Gist options
  • Save sertraline/709e39d7038a85e9e3e6bca1f8ca2a0d to your computer and use it in GitHub Desktop.
Save sertraline/709e39d7038a85e9e3e6bca1f8ca2a0d to your computer and use it in GitHub Desktop.
Vue+Vuetify phone mask w/o dependencies
<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>
<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