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:
formsdev
2023-09-18 18:42:05 +05:30
committed by GitHub
parent 08db014cde
commit d75975bdec
10 changed files with 257 additions and 95 deletions

View File

@@ -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>

View File

@@ -69,14 +69,11 @@
<script>
import FormLogicPropertyResolver from '../../../forms/FormLogicPropertyResolver.js'
import FormPendingSubmissionKey from '../../../mixins/forms/form-pending-submission-key.js'
import PhoneInput from '../../forms/PhoneInput.vue'
import {mapState} from "vuex";
export default {
name: 'OpenFormField',
components: {
PhoneInput
},
components: {},
mixins: [FormPendingSubmissionKey],
props: {
form: {

View File

@@ -216,6 +216,11 @@
:date-range="field.date_range===true"
label="Pre-filled value"
/>
<phone-input v-else-if="field.type === 'phone_number'"
name="prefill" class="mt-3"
:form="field" :can-only-country="true"
label="Pre-filled value"
/>
<text-area-input v-else-if="field.type === 'text' && field.multi_lines"
name="prefill" class="mt-3"
:form="field"
@@ -267,7 +272,7 @@
@input="onFieldHelpPositionChange"
/>
<template v-if="['text','number','url','email','phone_number'].includes(field.type)">
<template v-if="['text','number','url','email'].includes(field.type)">
<text-input v-model="field.max_char_limit" name="max_char_limit" native-type="number" :min="1" :max="2000"
:form="field"
label="Max character limit"
@@ -401,7 +406,7 @@ export default {
},
mounted() {
if (['text', 'number', 'url', 'email', 'phone_number'].includes(this.field?.type) && !this.field?.max_char_limit) {
if (['text', 'number', 'url', 'email'].includes(this.field?.type) && !this.field?.max_char_limit) {
this.field.max_char_limit = 2000
}
},