monacousa-portal/components/EnhancedPhoneInput.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>