react-native-phone-number-input
Version:
Phone Number Input Component
248 lines (234 loc) • 7.35 kB
JavaScript
import React, { PureComponent } from "react";
import { View, Text, TouchableOpacity, Image, TextInput } from "react-native";
import CountryPicker, {
getCallingCode,
DARK_THEME,
DEFAULT_THEME,
CountryModalProvider,
Flag,
} from "react-native-country-picker-modal";
import { PhoneNumberUtil } from "google-libphonenumber";
import styles from "./styles";
const dropDown =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAi0lEQVRYR+3WuQ6AIBRE0eHL1T83FBqU5S1szdiY2NyTKcCAzU/Y3AcBXIALcIF0gRPAsehgugDEXnYQrUC88RIgfpuJ+MRrgFmILN4CjEYU4xJgFKIa1wB6Ec24FuBFiHELwIpQxa0ALUId9wAkhCnuBdQQ5ngP4I9wxXsBDyJ9m+8y/g9wAS7ABW4giBshQZji3AAAAABJRU5ErkJggg==";
const phoneUtil = PhoneNumberUtil.getInstance();
export default class PhoneInput extends PureComponent {
constructor(props) {
super(props);
this.state = {
code: props.defaultCode ? undefined : "91",
number: props.value
? props.value
: props.defaultValue
? props.defaultValue
: "",
modalVisible: false,
countryCode: props.defaultCode ? props.defaultCode : "IN",
disabled: props.disabled || false,
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.disabled !== prevState.disabled) {
if ((nextProps.value || nextProps.value === "") && nextProps.value !== prevState.number) {
return ({ disabled: nextProps.disabled, number: nextProps.value });
}
return ({ disabled: nextProps.disabled });
}
return null;
};
async componentDidMount() {
const { defaultCode } = this.props;
if (defaultCode) {
const code = await getCallingCode(defaultCode);
this.setState({ code });
}
}
getCountryCode = () => {
return this.state.countryCode;
};
getCallingCode = () => {
return this.state.code;
};
isValidNumber = (number) => {
try {
const { countryCode } = this.state;
const parsedNumber = phoneUtil.parse(number, countryCode);
return phoneUtil.isValidNumber(parsedNumber);
} catch (err) {
return false;
}
};
onSelect = (country) => {
const { onChangeCountry } = this.props;
this.setState(
{
countryCode: country.cca2,
code: country.callingCode[0],
},
() => {
const { onChangeFormattedText } = this.props;
if (onChangeFormattedText) {
if (country.callingCode[0]) {
onChangeFormattedText(
`+${country.callingCode[0]}${this.state.number}`
);
} else {
onChangeFormattedText(this.state.number);
}
}
}
);
if (onChangeCountry) {
onChangeCountry(country);
}
};
onChangeText = (text) => {
this.setState({ number: text });
const { onChangeText, onChangeFormattedText } = this.props;
if (onChangeText) {
onChangeText(text);
}
if (onChangeFormattedText) {
const { code } = this.state;
if (code) {
onChangeFormattedText(text.length > 0 ? `+${code}${text}` : text);
} else {
onChangeFormattedText(text);
}
}
};
getNumberAfterPossiblyEliminatingZero() {
let { number, code } = this.state;
if (number.length > 0 && number.startsWith("0")) {
number = number.substr(1);
return { number, formattedNumber: code ? `+${code}${number}` : number };
} else {
return { number, formattedNumber: code ? `+${code}${number}` : number };
}
}
renderDropdownImage = () => {
return (
<Image
source={{ uri: dropDown }}
resizeMode="contain"
style={styles.dropDownImage}
/>
);
};
renderFlagButton = (props) => {
const { layout = "first", flagSize } = this.props;
const { countryCode } = this.state;
if (layout === "first") {
return (
<Flag
countryCode={countryCode}
flagSize={flagSize ? flagSize : DEFAULT_THEME.flagSize}
/>
);
}
return <View />;
};
render() {
const {
withShadow,
withDarkTheme,
codeTextStyle,
textInputProps,
textInputStyle,
autoFocus,
placeholder,
disableArrowIcon,
flagButtonStyle,
containerStyle,
textContainerStyle,
renderDropdownImage,
countryPickerProps = {},
filterProps = {},
countryPickerButtonStyle,
layout = "first",
} = this.props;
const { modalVisible, code, countryCode, number, disabled } = this.state;
return (
<CountryModalProvider>
<View
style={[
styles.container,
withShadow ? styles.shadow : {},
containerStyle ? containerStyle : {},
]}
>
<TouchableOpacity
style={[
styles.flagButtonView,
layout === "second" ? styles.flagButtonExtraWidth : {},
flagButtonStyle ? flagButtonStyle : {},
countryPickerButtonStyle ? countryPickerButtonStyle : {},
]}
disabled={disabled}
onPress={() => this.setState({ modalVisible: true })}
>
<CountryPicker
onSelect={this.onSelect}
withEmoji
withFilter
withFlag
filterProps={filterProps}
countryCode={countryCode}
withCallingCode
disableNativeModal={disabled}
visible={modalVisible}
theme={withDarkTheme ? DARK_THEME : DEFAULT_THEME}
renderFlagButton={this.renderFlagButton}
onClose={() => this.setState({ modalVisible: false })}
{...countryPickerProps}
/>
{code && layout === "second" && (
<Text
style={[styles.codeText, codeTextStyle ? codeTextStyle : {}]}
>{`+${code}`}</Text>
)}
{!disableArrowIcon && (
<React.Fragment>
{renderDropdownImage
? renderDropdownImage
: this.renderDropdownImage()}
</React.Fragment>
)}
</TouchableOpacity>
<View
style={[
styles.textContainer,
textContainerStyle ? textContainerStyle : {},
]}
>
{code && layout === "first" && (
<Text
style={[styles.codeText, codeTextStyle ? codeTextStyle : {}]}
>{`+${code}`}</Text>
)}
<TextInput
style={[styles.numberText, textInputStyle ? textInputStyle : {}]}
placeholder={placeholder ? placeholder : "Phone Number"}
onChangeText={this.onChangeText}
value={number}
editable={disabled ? false : true}
selectionColor="black"
keyboardAppearance={withDarkTheme ? "dark" : "default"}
keyboardType="number-pad"
autoFocus={autoFocus}
{...textInputProps}
/>
</View>
</View>
</CountryModalProvider>
);
}
}
export const isValidNumber = (number, countryCode) => {
try {
const parsedNumber = phoneUtil.parse(number, countryCode);
return phoneUtil.isValidNumber(parsedNumber);
} catch (err) {
return false;
}
};