react-phone-number-input
Version: 
Telephone number input React component
199 lines (172 loc) • 7.67 kB
TypeScript
// React TypeScript Cheatsheet doesn't recommend using `React.FunctionalComponent` (`React.FC`).
// https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components
import * as React from 'react';
import {
	CountryCode,
	E164Number,
	MetadataJson
} from 'libphonenumber-js/core';
export type Metadata = MetadataJson;
export type Value = E164Number;
export type ExternalValue = string;
// `Country` type could be used in the application's code.
export type Country = CountryCode;
type Locale = string;
type LocaleProperty = Locale | Locale[];
type CountryOption = 'XX' | '🌐' | '|' | '...' | '…' | Country;
// `Flags` are imported in `flags/index.d.ts`.
export type Flags = Partial<Record<Country, EmbeddedFlag>>;
export interface EmbeddedFlagProps {
	title: string;
}
type EmbeddedFlag = (props: EmbeddedFlagProps) => JSX.Element;
export interface FlagProps {
	country: Country;
	countryName: string;
	flagUrl?: string;
	flags?: Flags;
}
type Flag = (props: FlagProps) => JSX.Element;
// `LabelKey` is imported in `/locale/{locale}.json.d.ts`.
export type LabelKey = Country | 'ZZ' | 'ext' | 'country' | 'phone';
// `Labels` are imported in `/core/index.d.ts`.
export type Labels = Partial<Record<LabelKey, string>>;
// export type Labels = Partial<Record<Country, string>> & {
//   ZZ: string?,
//   ext: string?,
//   country: string?,
//   phone: string?,
// }
// type InputFormat =
// 	'INTERNATIONAL' |
// 	'NATIONAL_PART_OF_INTERNATIONAL' |
// 	'NATIONAL' |
// 	'INTERNATIONAL_OR_NATIONAL'
// `FeatureProps` are imported in:
// * `/react-hook-form/index.d.ts`
//
// `Props` extend `FeatureProps` by adding `value` and `onChange` properties.
//
// The `Props` interface extends `React.InputHTMLAttributes<HTMLInputElement>`
// in order to support "rest" props (any other props not used by this library).
//
// `Omit<..., 'onChange' | 'value'>` is added in order to omit the standard
// `onChange(event: Event)` and `value: string` HTML attributes
// because this component uses its own with different signatures:
// `onChange(value?: Value)` and `value?: Value`.
// Because the signatures are different, those two standard HTML attributes
// wouldn't get replaced with the ones used by this library,
// resulting in the `Props` interface allowing two types of both
// `onChange` and `value` while only one of each would be valid to pass.
//
// This `Props` interface can only be used in an HTML DOM environment
// because it extends `React.InputHTMLAttributes<HTMLInputElement>`.
//
export type FeatureProps<InputComponentProps> = Omit<InputComponentProps, 'value' | 'onChange'> & {
	onFocus?(event: React.FocusEvent<HTMLElement>): void;
	onBlur?(event: React.FocusEvent<HTMLElement>): void;
	disabled?: boolean;
	readOnly?: boolean;
	autoComplete?: string;
	initialValueFormat?: 'national';
	defaultCountry?: Country;
	countries?: Country[];
	labels?: Labels;
	locales?: LocaleProperty;
	flagUrl?: string;
	flags?: Flags;
	flagComponent?: Flag;
	addInternationalOption?: boolean;
	internationalIcon?: React.ElementType;
	countryOptionsOrder?: CountryOption[];
	style?: object;
	className?: string;
	countrySelectComponent?: React.ElementType;
	countrySelectProps?: object;
	inputComponent?: React.ElementType;
	numberInputProps?: object;
	containerComponent?: React.ElementType;
	containerComponentProps?: object;
	smartCaret?: boolean;
	international?: boolean;
	limitMaxLength?: boolean;
	countryCallingCodeEditable?: boolean;
	onCountryChange?(country?: Country): void;
	focusInputOnCountrySelection?: boolean;
}
// `Props` are imported in:
// * `/core/index.d.ts`
export type Props<InputComponentProps> = FeatureProps<InputComponentProps> & {
	// The `value` type could be `Value` or `string`.
	// `string` was specifically added to allow for passing the `value` property
	// that was received from an external source such as a database.
	// https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/144#note_1921382409
	value?: Value | ExternalValue;
	onChange(value?: Value): void;
}
// `State` is imported in:
// * `/core/index.d.ts`
// * `/react-hook-form/index.d.ts`
export interface State<Props> {
	// The currently selected country: either automatically-selected or user-selected.
	country?: Country;
	// This is basically `props.countries` with some additional filtering
	// that removes possibly non-existing country codes.
	// For example, if `props.countries` is `["US", "XX"]` then `state.countries` is `["US"]`.
	countries?: Country[];
	// Tracks the latest country that the user has selected themself.
	// This could be used when deciding which country flag to display
	// when the component can't decide between a set of matching countries.
	// For example, if the user has selected CA and then starts inputting a phone number
	// and at some stage the phone number is determined to belong to US country,
	// but then the user reconsiders and erases some of the digits — it should perhaps
	// show CA flag instead of the US flag in such "both countries are possible" situation.
	latestCountrySelectedByUser?: Country;
	// If the user has already manually selected a country via the country select
	// then don't override that already-selected country if the `defaultCountry` property
	// value changes for some reason. The `hasUserSelectedACountry` flag is used to track that:
	// whether the user has already selected any country or whether they haven't.
	hasUserSelectedACountry?: boolean;
	// The `value` property is duplicated in `state` in order to determine
	// whether the `value` property has changed or not. Specifically, it is used
	// in `getDerivedStateFromProps()` function to ignore a `getDerivedStateFromProps()` call
	// that happens in `this.onChange()` right after `this.setState()`.
	// If that `getDerivedStateFromProps()` call wasn't ignored then the country flag would
	// reset itself on each input value change event.
	value?: Value;
	// `phoneDigits` are the parsed phone number digits,
	// including a leading `+`, if present.
	// Examples of `phoneDigits`:
	// * `undefined`
	// * "+78005553535"
	// * "88005553535"
	phoneDigits?: string;
	// `forceRerender` is a "dummy" object that is set to `{}`
	// in order to force a rerender of the component.
	forceRerender?: object;
	isFocused?: boolean;
	// `props` are stored in state in order to be able to compare
	// new `props` with the "previous" ones in `state.props`
	// in `PhoneInputWithCountry.getDerivedStateFromProps()`.
	props: Props;
}
// export type DefaultInputComponentProps = React.InputHTMLAttributes<HTMLInputElement>
// Precise TypeScript "typings" turned out to be too complex to figure out,
// so it just allows any property that a hypothetical custom `inputComponent` could accept.
export type DefaultInputComponentProps = {
	[anyProperty: string]: any;
}
type PhoneInputWithCountrySelectType<InputComponentProps = DefaultInputComponentProps> = React.ComponentClass<Props<InputComponentProps>, State<Props<InputComponentProps>>>
declare const PhoneInputWithCountrySelect: PhoneInputWithCountrySelectType;
export default PhoneInputWithCountrySelect;
export function formatPhoneNumber(value: Value | ExternalValue): string;
export function formatPhoneNumberIntl(value: Value | ExternalValue): string;
export {
	default as parsePhoneNumber,
	isValidPhoneNumber,
	isPossiblePhoneNumber,
	getCountryCallingCode,
	getCountries,
	isSupportedCountry,
	PhoneNumber
} from 'libphonenumber-js/min';