import { useEffect, useRef, useCallback } from 'react'; import { Box, InputBase, InputAdornment, Typography } from '@mui/material'; import { defaultCountries, usePhoneInput } from 'react-international-phone'; import { useMount } from 'ahooks'; import CountrySelect from './country-select'; import { isValidCountry } from '../libs/util'; import { getPhoneUtil } from '../libs/phone-validator'; export interface PhoneFieldProps { value: string; country: string; onChange: (phone: string) => void; onCountryChange: (country: string) => void; label: string; error?: string; onBlur?: () => void; } /** * Phone input with country flag + dial code selector. * Standalone version of V1 PhoneInput — no react-hook-form dependency. */ export default function PhoneField({ value, country: externalCountry, onChange, onCountryChange, label, error = undefined, onBlur = undefined, }: PhoneFieldProps) { const isUpdatingRef = useRef(false); const safeUpdate = useCallback((callback: () => void) => { if (isUpdatingRef.current) return; try { isUpdatingRef.current = true; callback(); } finally { requestAnimationFrame(() => { isUpdatingRef.current = false; }); } }, []); const { phone, handlePhoneValueChange, inputRef, country, setCountry } = usePhoneInput({ defaultCountry: isValidCountry(externalCountry) ? externalCountry : 'us', value: value || '', countries: defaultCountries, onChange: (data) => { safeUpdate(() => { onChange(data.phone); onCountryChange(data.country); }); }, }); // Preload phone validator (matches V1 behavior) useMount(() => { getPhoneUtil().catch((err) => { console.error('Failed to preload phone validator:', err); }); }); // Sync external country changes (e.g. from postal code CountrySelect) useEffect(() => { if (!externalCountry || externalCountry === country) return; safeUpdate(() => { setCountry(externalCountry); }); }, [externalCountry, country, setCountry, safeUpdate]); const handleCountryChange = useCallback( (v: string) => { safeUpdate(() => { setCountry(v); onCountryChange(v); }); }, [setCountry, safeUpdate, onCountryChange] ); return ( {label} } sx={{ bgcolor: (theme) => (theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.06)' : 'grey.50'), borderRadius: '8px', px: 1.5, py: 0.75, fontSize: 14, '& .MuiInputBase-input': { p: 0 }, }} /> {error && {error}} ); }