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';