Fix phone input (#201)
* Fix phone input * remove extra * fix factory * Fix phone input * Validate phone number rule * Prefill support for country only * Fix phone input error * fix tests --------- Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
@@ -12,10 +12,12 @@
|
||||
<slot name="help"><span class="field-help" v-html="help"/></slot>
|
||||
</small>
|
||||
</div>
|
||||
<div :id="id ? id : name" :disabled="disabled" :name="name" :style="inputStyle" class="flex items-center">
|
||||
<v-select class="w-[110px]" dropdown-class="w-[400px]" input-class="rounded-r-none" :data="countries" :value="selectedCountryCode"
|
||||
:searchable="true" :search-keys="['name']" :option-key="'code'" :color="color"
|
||||
:placeholder="'Select a country'" :uppercase-labels="true" :theme="theme" @input="onCountryChange">
|
||||
<div :id="id ? id : name" :name="name" :style="inputStyle" class="flex items-center">
|
||||
<v-select class="w-[110px]" dropdown-class="w-[400px]" input-class="rounded-r-none" :data="countries"
|
||||
v-model="selectedCountryCode"
|
||||
:has-error="hasValidation && form.errors.has(name)"
|
||||
:disabled="disabled" :searchable="true" :search-keys="['name']" :option-key="'code'" :color="color"
|
||||
:placeholder="'Select a country'" :uppercase-labels="true" :theme="theme" @input="onChangeCountryCode">
|
||||
<template #option="props">
|
||||
<div class="flex items-center space-x-2 hover:text-white">
|
||||
<country-flag size="normal" class="!-mt-[9px]" :country="props.option.code"/>
|
||||
@@ -25,15 +27,21 @@
|
||||
</template>
|
||||
<template #selected="props">
|
||||
<div class="flex items-center space-x-2 justify-center overflow-hidden">
|
||||
<country-flag size="normal" class="!-mt-[9px]" :country="props.option.code" />
|
||||
<country-flag size="normal" class="!-mt-[9px]" :country="props.option.code"/>
|
||||
<span>{{ props.option.dial_code }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</v-select>
|
||||
<input v-model="inputVal" type="text" class="inline-flex-grow !border-l-0 !rounded-l-none"
|
||||
<input v-model="inputVal" type="text" class="inline-flex-grow !border-l-0 !rounded-l-none" :disabled="disabled"
|
||||
:class="[theme.default.input, { '!ring-red-500 !ring-2': hasValidation && form.errors.has(name), '!cursor-not-allowed !bg-gray-200': disabled }]"
|
||||
:placeholder="placeholder" :style="inputStyle" @input="onInput">
|
||||
</div>
|
||||
<div v-if="help && helpPosition=='below_input'" class="flex">
|
||||
<small :class="theme.default.help" class="grow">
|
||||
<slot name="help"><span class="field-help" v-html="help"/></slot>
|
||||
</small>
|
||||
</div>
|
||||
<has-error v-if="hasValidation" :form="form" :field="name"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -42,32 +50,49 @@ import {directive as onClickaway} from 'vue-clickaway'
|
||||
import inputMixin from '~/mixins/forms/input.js'
|
||||
import countryCodes from '../../../data/country_codes.json'
|
||||
import CountryFlag from 'vue-country-flag'
|
||||
import VSelect from './components/VSelect.vue'
|
||||
import parsePhoneNumber from 'libphonenumber-js'
|
||||
|
||||
export default {
|
||||
phone: 'PhoneInput',
|
||||
components: {
|
||||
CountryFlag, VSelect
|
||||
},
|
||||
components: {CountryFlag},
|
||||
directives: {
|
||||
onClickaway: onClickaway
|
||||
},
|
||||
mixins: [inputMixin],
|
||||
props: {
|
||||
canOnlyCountry: {type: Boolean, default: false}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
selectedCountryCode: countryCodes[234],
|
||||
selectedCountryCode: this.getCountryBy('US'), // Default US
|
||||
countries: countryCodes,
|
||||
isOpen: false,
|
||||
inputVal: ''
|
||||
inputVal: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
inputVal(newVal, oldVal) {
|
||||
if (newVal.startsWith('0')) {
|
||||
newVal = newVal.replace(/^0+/, '')
|
||||
|
||||
mounted() {
|
||||
if (this.compVal) {
|
||||
const phoneObj = parsePhoneNumber(this.compVal)
|
||||
if (phoneObj !== undefined && phoneObj) {
|
||||
if (phoneObj.country !== undefined && phoneObj.country) {
|
||||
this.selectedCountryCode = this.getCountryBy(phoneObj.country)
|
||||
}
|
||||
this.inputVal = phoneObj.nationalNumber
|
||||
} else if (this.compVal) {
|
||||
this.selectedCountryCode = this.getCountryBy(this.compVal, 'dial_code')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
inputVal: {
|
||||
handler(val) {
|
||||
if (val && val.startsWith('0')) {
|
||||
val = val.substring(1)
|
||||
}
|
||||
this.compVal = (val) ? this.selectedCountryCode.dial_code + val : null
|
||||
}
|
||||
this.compVal = this.selectedCountryCode.dial_code + ' ' + newVal
|
||||
},
|
||||
selectedCountryCode(newVal, oldVal) {
|
||||
if (this.compVal) {
|
||||
@@ -76,18 +101,19 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onCountryChange(country) {
|
||||
this.selectedCountryCode = country
|
||||
this.closeDropdown()
|
||||
},
|
||||
closeDropdown() {
|
||||
this.isOpen = false
|
||||
getCountryBy(code, type = 'code') {
|
||||
return countryCodes.find((item) => {
|
||||
return item[type] === code
|
||||
})
|
||||
},
|
||||
onInput(event) {
|
||||
const input = event.target.value
|
||||
const digitsOnly = input.replace(/[^0-9]/g, '')
|
||||
this.inputVal = digitsOnly
|
||||
this.inputVal = event.target.value.replace(/[^0-9]/g, '')
|
||||
},
|
||||
onChangeCountryCode() {
|
||||
if (this.canOnlyCountry && (this.inputVal === null || this.inputVal === '' || !this.inputVal)) {
|
||||
this.compVal = this.selectedCountryCode.dial_code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user