1- import { useMemo , useState } from "react" ;
1+ import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
22import ReactPhoneInput from "react-phone-input-2" ;
33
4- import { ParsePhoneNumber , PhoneInputProps , ReactPhoneOnChange , ReactPhoneOnMount } from "../types" ;
5-
6- import "./style5.css" ;
4+ import { CountryData , PhoneInputProps , PhoneNumber , ReactPhoneOnChange , ReactPhoneOnMount } from "../types" ;
75
6+ import styleInject from "./style" ;
87import masks from "./phoneMasks.json" ;
98import timezones from "./timezones.json" ;
109import validations from "./validations.json" ;
1110
11+ styleInject ( "style5.css" ) ;
12+
13+ let browserLoaded = true ;
14+
1215type ISO2Code = keyof typeof masks ;
1316type Timezone = keyof typeof timezones ;
1417
@@ -18,7 +21,7 @@ const getDefaultISO2Code = () => {
1821 return ( timezones [ timezone ] || "" ) . toLowerCase ( ) || "us" ;
1922}
2023
21- const parsePhoneNumber : ParsePhoneNumber = ( value , data , formattedNumber ) => {
24+ const parsePhoneNumber = ( value : string , data : CountryData , formattedNumber : string ) : PhoneNumber => {
2225 const isoCode = data ?. countryCode ;
2326 const countryCodePattern = / \+ \d + / ;
2427 const areaCodePattern = / \( ( \d + ) \) / ;
@@ -42,14 +45,7 @@ const parsePhoneNumber: ParsePhoneNumber = (value, data, formattedNumber) => {
4245 const phoneNumberMatch = value ? ( value . match ( phoneNumberPattern ) || [ ] ) : [ ] ;
4346 const phoneNumber = phoneNumberMatch . length > 1 ? phoneNumberMatch [ 1 ] : null ;
4447
45- /** Checks if both the area code and phone number length satisfy the validation rules */
46- const rules = validations [ isoCode as ISO2Code ] || { areaCode : [ ] , phoneNumber : [ ] } ;
47- const valid = [
48- rules . areaCode . includes ( ( areaCode || "" ) . toString ( ) . length ) ,
49- rules . phoneNumber . includes ( ( phoneNumber || "" ) . toString ( ) . length ) ,
50- ] . every ( Boolean ) ;
51-
52- return { countryCode, areaCode, phoneNumber, isoCode, valid, dialChanged} ;
48+ return { countryCode, areaCode, phoneNumber, isoCode, dialChanged} ;
5349}
5450
5551const PhoneInput = ( {
@@ -64,6 +60,9 @@ const PhoneInput = ({
6460 inputClass : inputClassProxy ,
6561 ...reactPhoneInputProps
6662 } : PhoneInputProps ) => {
63+ const loaded = useRef ( browserLoaded ) ;
64+ const reset = useRef ( false ) ;
65+ const initialized = useRef ( false ) ;
6766 const [ currentCode , setCurrentCode ] = useState ( "" ) ;
6867
6968 const countryCode = useMemo ( ( ) => country || getDefaultISO2Code ( ) , [ country ] ) ;
@@ -79,29 +78,50 @@ const PhoneInput = ({
7978 return inputClassProxy ? `${ className } ${ inputClassProxy } ` : className ;
8079 } , [ inputClassProxy , size ] ) ;
8180
82- const onChange : ReactPhoneOnChange = ( value , data , event , formattedNumber ) => {
81+ const checkValidity = ( metadata : PhoneNumber ) => {
82+ /** Checks if both the area code and phone number length satisfy the validation rules */
83+ const rules = validations [ metadata . isoCode as ISO2Code ] || { areaCode : [ ] , phoneNumber : [ ] } ;
84+ const isValid = reset . current || ( ( loaded . current || initialized . current ) ? [
85+ rules . areaCode . includes ( ( metadata . areaCode || "" ) . toString ( ) . length ) ,
86+ rules . phoneNumber . includes ( ( metadata . phoneNumber || "" ) . toString ( ) . length ) ,
87+ ] . every ( Boolean ) : ! initialized . current ) ;
88+ initialized . current = true ;
89+ loaded . current = false ;
90+ reset . current = false ;
91+ return isValid ;
92+ }
93+
94+ const onChange : ReactPhoneOnChange = useCallback ( ( value , data , event , formattedNumber ) => {
8395 const { dialChanged, ...metadata } = parsePhoneNumber ( value , data , formattedNumber ) ;
8496 const code = metadata . isoCode as ISO2Code ;
8597
8698 if ( code !== currentCode ) {
8799 /** Clears phone number when the country is selected manually */
88100 metadata . areaCode = dialChanged ? null : metadata . areaCode ;
89101 metadata . phoneNumber = null ;
90- metadata . valid = false ;
91102 setCurrentCode ( code ) ;
92103 }
93104
94- handleChange ( metadata , event ) ;
95- }
105+ handleChange ( { ... metadata , valid : ( ) => checkValidity ( metadata ) } , event ) ;
106+ } , [ currentCode , handleChange ] ) ;
96107
97- const onMount : ReactPhoneOnMount = ( rawValue , { countryCode, ...event } , formattedNumber ) => {
108+ const onMount : ReactPhoneOnMount = useCallback ( ( rawValue , { countryCode, ...event } , formattedNumber ) => {
98109 const { dialChanged, ...metadata } = parsePhoneNumber ( rawValue , { countryCode} , formattedNumber ) ;
99- /** Initiates the current country code with the code of initial value */
100- setCurrentCode ( metadata . isoCode as ISO2Code ) ;
101110 /** Initializes the existing value */
102- handleChange ( metadata , event ) ;
103- handleMount ( metadata ) ;
104- }
111+ handleChange ( { ...metadata , valid : ( ) => checkValidity ( metadata ) } , event ) ;
112+ handleMount ( { ...metadata , valid : ( ) => checkValidity ( metadata ) } ) ;
113+ /** Sets the current country code to the code of the initial value */
114+ setCurrentCode ( metadata . isoCode as ISO2Code ) ;
115+ if ( loaded . current && ! initialized . current ) reset . current = true ;
116+ initialized . current = false ;
117+ } , [ handleChange , handleMount ] ) ;
118+
119+ useEffect ( ( ) => {
120+ reset . current = ! browserLoaded ;
121+ return ( ) => {
122+ browserLoaded = false ;
123+ }
124+ } , [ ] ) ;
105125
106126 return (
107127 < ReactPhoneInput
0 commit comments