148 lines
3.3 KiB
Vue
148 lines
3.3 KiB
Vue
<template>
|
|
<div class="enhanced-phone-input">
|
|
<PhoneInput
|
|
v-model="phoneValue"
|
|
:country-code="selectedCountryCode"
|
|
:placeholder="placeholder || 'Enter phone number'"
|
|
:preferred-countries="['US', 'FR', 'GB', 'DE', 'CA', 'AU', 'ES', 'IT', 'NL', 'BE']"
|
|
:auto-format="true"
|
|
:no-formatting-as-you-type="false"
|
|
country-locale="en-US"
|
|
@update="handlePhoneUpdate"
|
|
@input="handlePhoneInput"
|
|
class="phone-input-wrapper"
|
|
>
|
|
<template #input="{ inputValue, updateInputValue, placeholder: inputPlaceholder }">
|
|
<v-text-field
|
|
:model-value="inputValue"
|
|
@update:model-value="updateInputValue"
|
|
:placeholder="inputPlaceholder"
|
|
:label="label"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
:error="hasError"
|
|
:error-messages="errorMessage"
|
|
:rules="rules"
|
|
class="phone-number-input"
|
|
hide-details="auto"
|
|
/>
|
|
</template>
|
|
</PhoneInput>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import PhoneInput from 'base-vue-phone-input';
|
|
import type { Results as PhoneResults } from 'base-vue-phone-input';
|
|
|
|
interface Props {
|
|
modelValue?: string;
|
|
label?: string;
|
|
placeholder?: string;
|
|
error?: boolean;
|
|
errorMessage?: string;
|
|
rules?: Array<(value: any) => boolean | string>;
|
|
}
|
|
|
|
interface Emits {
|
|
(e: 'update:model-value', value: string): void;
|
|
(e: 'phone-data', data: PhoneResults): void;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
modelValue: '',
|
|
label: 'Phone Number',
|
|
placeholder: 'Enter phone number',
|
|
error: false,
|
|
errorMessage: '',
|
|
rules: () => []
|
|
});
|
|
|
|
const emit = defineEmits<Emits>();
|
|
|
|
// Local state
|
|
const phoneValue = ref(props.modelValue);
|
|
const selectedCountryCode = ref('US');
|
|
const phoneData = ref<PhoneResults | null>(null);
|
|
|
|
// Computed properties
|
|
const hasError = computed(() => props.error);
|
|
|
|
// Watch for external changes to modelValue
|
|
watch(() => props.modelValue, (newValue) => {
|
|
if (newValue !== phoneValue.value) {
|
|
phoneValue.value = newValue;
|
|
}
|
|
});
|
|
|
|
// Handle phone input events
|
|
const handlePhoneInput = (value: string) => {
|
|
phoneValue.value = value;
|
|
emit('update:model-value', value);
|
|
};
|
|
|
|
const handlePhoneUpdate = (data: PhoneResults) => {
|
|
phoneData.value = data;
|
|
|
|
// Update country code if available
|
|
if (data.countryCode) {
|
|
selectedCountryCode.value = data.countryCode;
|
|
}
|
|
|
|
// Emit the formatted phone number
|
|
const formattedNumber = data.formatInternational || data.e164 || phoneValue.value;
|
|
if (formattedNumber !== phoneValue.value) {
|
|
phoneValue.value = formattedNumber;
|
|
emit('update:model-value', formattedNumber);
|
|
}
|
|
|
|
// Emit phone data for parent component
|
|
emit('phone-data', data);
|
|
};
|
|
|
|
// Initialize with modelValue
|
|
onMounted(() => {
|
|
if (props.modelValue) {
|
|
phoneValue.value = props.modelValue;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.enhanced-phone-input {
|
|
width: 100%;
|
|
}
|
|
|
|
.phone-input-wrapper {
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.country-select {
|
|
flex: 0 0 120px;
|
|
}
|
|
|
|
.phone-number-input {
|
|
flex: 1;
|
|
}
|
|
|
|
.flag-emoji {
|
|
font-size: 1.2em;
|
|
}
|
|
|
|
.country-code {
|
|
font-size: 0.875rem;
|
|
color: rgba(var(--v-theme-on-surface), 0.87);
|
|
}
|
|
|
|
/* Ensure proper spacing in Vuetify forms */
|
|
:deep(.v-input) {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
:deep(.v-input__details) {
|
|
min-height: 20px;
|
|
}
|
|
</style>
|