UNPKG

react-phone-number-input

Version:

Telephone number input React component

913 lines (903 loc) 43 kB
var _excluded = ["name", "disabled", "readOnly", "autoComplete", "style", "className", "inputRef", "inputComponent", "numberInputProps", "smartCaret", "countrySelectComponent", "countrySelectProps", "containerComponent", "containerComponentProps", "defaultCountry", "countries", "countryOptionsOrder", "labels", "flags", "flagComponent", "flagUrl", "addInternationalOption", "internationalIcon", "displayInitialValueAsLocalNumber", "initialValueFormat", "onCountryChange", "limitMaxLength", "countryCallingCodeEditable", "focusInputOnCountrySelection", "reset", "metadata", "international", "locales"]; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import InputSmart from './InputSmart.js'; import InputBasic from './InputBasic.js'; import { CountrySelectWithIcon as CountrySelect } from './CountrySelect.js'; import Flag from './Flag.js'; import InternationalIcon from './InternationalIcon.js'; import { validateE164Number } from './helpers/isE164Number.js'; import { sortCountryOptions, isCountrySupportedWithError, getSupportedCountries, getSupportedCountryOptions, getCountries } from './helpers/countries.js'; import { createCountryIconComponent } from './CountryIcon.js'; import { setRefsValue } from './useExternalRef.js'; import { metadata as metadataPropType, labels as labelsPropType } from './PropTypes.js'; import { getPreSelectedCountry, getCountrySelectOptions as _getCountrySelectOptions, couldNumberBelongToCountry, parsePhoneNumber, generateNationalNumberDigits, getPhoneDigitsForNewCountry, getInitialPhoneDigits, onPhoneDigitsChange, e164 } from './helpers/phoneInputHelpers.js'; import getPhoneInputWithCountryStateUpdateFromNewProps from './helpers/getPhoneInputWithCountryStateUpdateFromNewProps.js'; var PhoneNumberInput_ = /*#__PURE__*/function (_React$PureComponent) { function PhoneNumberInput_(props) { var _this; _classCallCheck(this, PhoneNumberInput_); _this = _callSuper(this, PhoneNumberInput_, [props]); // This function mimicks `refSetter` function returned from `useExternalRef()` hook // because this class-like React component can't use the `useExternalRef()` hook. _defineProperty(_this, "setInputRef", function (instance) { setRefsValue([_this.props.inputRef, _this.inputRef], instance); }); // A shorthand for not passing `metadata` as a second argument. _defineProperty(_this, "isCountrySupportedWithError", function (country) { var metadata = _this.props.metadata; return isCountrySupportedWithError(country, metadata); }); // Country `<select/>` `onChange` handler. _defineProperty(_this, "onCountryChange", function (newCountry) { var _this$props = _this.props, international = _this$props.international, metadata = _this$props.metadata, onChange = _this$props.onChange, focusInputOnCountrySelection = _this$props.focusInputOnCountrySelection; var _this$state = _this.state, prevPhoneDigits = _this$state.phoneDigits, prevCountry = _this$state.country; // After the new `country` has been selected, // if the phone number `<input/>` holds any digits // then migrate those digits for the new `country`. var newPhoneDigits = getPhoneDigitsForNewCountry(prevPhoneDigits, { prevCountry: prevCountry, newCountry: newCountry, metadata: metadata, // Convert the phone number to "national" format // when the user changes the selected country by hand. useNationalFormat: !international }); var newValue = e164(newPhoneDigits, newCountry, metadata); // Focus phone number `<input/>` upon country selection. if (focusInputOnCountrySelection) { _this.inputRef.current.focus(); } // If the user has already manually selected a country // then don't override that already selected country // if the `defaultCountry` property changes. // That's what `hasUserSelectedACountry` flag is for. _this.setState({ country: newCountry, latestCountrySelectedByUser: newCountry, hasUserSelectedACountry: true, phoneDigits: newPhoneDigits, value: newValue }, function () { // Update the new `value` property. // Doing it after the `state` has been updated // because `onChange()` will trigger `getDerivedStateFromProps()` // with the new `value` which will be compared to `state.value` there. onChange(newValue); }); }); /** * `<input/>` `onChange()` handler. * Updates `value` property accordingly (so that they are kept in sync). * @param {string?} input — Either a parsed phone number or an empty string. Examples: `""`, `"+"`, `"+123"`, `"123"`. */ _defineProperty(_this, "onChange", function (_phoneDigits) { var _this$props2 = _this.props, defaultCountry = _this$props2.defaultCountry, onChange = _this$props2.onChange, addInternationalOption = _this$props2.addInternationalOption, international = _this$props2.international, limitMaxLength = _this$props2.limitMaxLength, countryCallingCodeEditable = _this$props2.countryCallingCodeEditable, metadata = _this$props2.metadata; var _this$state2 = _this.state, countries = _this$state2.countries, prevPhoneDigits = _this$state2.phoneDigits, currentlySelectedCountry = _this$state2.country, latestCountrySelectedByUser = _this$state2.latestCountrySelectedByUser; var _onPhoneDigitsChange = onPhoneDigitsChange(_phoneDigits, { prevPhoneDigits: prevPhoneDigits, country: currentlySelectedCountry, countryRequired: !addInternationalOption, defaultCountry: defaultCountry, latestCountrySelectedByUser: latestCountrySelectedByUser, getAnyCountry: function getAnyCountry() { return _this.getFirstSupportedCountry({ countries: countries }); }, countries: countries, international: international, limitMaxLength: limitMaxLength, countryCallingCodeEditable: countryCallingCodeEditable, metadata: metadata }), phoneDigits = _onPhoneDigitsChange.phoneDigits, country = _onPhoneDigitsChange.country, value = _onPhoneDigitsChange.value; var stateUpdate = { phoneDigits: phoneDigits, value: value, country: country }; // Reset `latestCountrySelectedByUser` if it no longer fits the `value`. if (latestCountrySelectedByUser && value && !couldNumberBelongToCountry(value, latestCountrySelectedByUser, metadata)) { stateUpdate.latestCountrySelectedByUser = undefined; } if (countryCallingCodeEditable === false) { // If it simply did `setState({ phoneDigits: intlPrefix })` here, // then it would have no effect when erasing an inital international prefix // via Backspace, because `phoneDigits` in `state` wouldn't change // as a result, because it was `prefix` and it became `prefix`, // so the component wouldn't rerender, and the user would be able // to erase the country calling code part, and that part is // assumed to be non-eraseable. That's why the component is // forcefully rerendered here. // https://github.com/catamphetamine/react-phone-number-input/issues/367#issuecomment-721703501 if (!value && phoneDigits === _this.state.phoneDigits) { // Force a re-render of the `<input/>` in order to reset its value. stateUpdate.forceRerender = {}; } } _this.setState(stateUpdate, // Update the new `value` property. // Doing it after the `state` has been updated // because `onChange()` will trigger `getDerivedStateFromProps()` // with the new `value` which will be compared to `state.value` there. function () { return onChange(value); }); }); // Toggles the `--focus` CSS class. _defineProperty(_this, "_onFocus", function () { return _this.setState({ isFocused: true }); }); // Toggles the `--focus` CSS class. _defineProperty(_this, "_onBlur", function () { return _this.setState({ isFocused: false }); }); _defineProperty(_this, "onFocus", function (event) { _this._onFocus(); var onFocus = _this.props.onFocus; if (onFocus) { onFocus(event); } }); _defineProperty(_this, "onBlur", function (event) { var onBlur = _this.props.onBlur; _this._onBlur(); if (onBlur) { onBlur(event); } }); _defineProperty(_this, "onCountryFocus", function (event) { _this._onFocus(); // this.setState({ countrySelectFocused: true }) var countrySelectProps = _this.props.countrySelectProps; if (countrySelectProps) { var onFocus = countrySelectProps.onFocus; if (onFocus) { onFocus(event); } } }); _defineProperty(_this, "onCountryBlur", function (event) { _this._onBlur(); // this.setState({ countrySelectFocused: false }) var countrySelectProps = _this.props.countrySelectProps; if (countrySelectProps) { var onBlur = countrySelectProps.onBlur; if (onBlur) { onBlur(event); } } }); _this.inputRef = /*#__PURE__*/React.createRef(); var _this$props3 = _this.props, _value = _this$props3.value, labels = _this$props3.labels, _international = _this$props3.international, _addInternationalOption = _this$props3.addInternationalOption, displayInitialValueAsLocalNumber = _this$props3.displayInitialValueAsLocalNumber, initialValueFormat = _this$props3.initialValueFormat, _metadata = _this$props3.metadata; var _this$props4 = _this.props, _defaultCountry = _this$props4.defaultCountry, _countries = _this$props4.countries; // Validate `defaultCountry`. if (_defaultCountry) { if (!_this.isCountrySupportedWithError(_defaultCountry)) { _defaultCountry = undefined; } } // Validate that the initially-supplied `value` is in `E.164` format. // Because sometimes people attempt to supply a `value` like "+1 (879) 490-8676". // https://gitlab.com/catamphetamine/react-phone-number-input/-/issues/231#note_2016334796 if (_value) { validateE164Number(_value); } // Validate `countries`. _countries = getSupportedCountries(_countries, _metadata); var phoneNumber = parsePhoneNumber(_value, _metadata); _this.CountryIcon = createCountryIconComponent(_this.props); var preSelectedCountry = getPreSelectedCountry({ value: _value, phoneNumber: phoneNumber, defaultCountry: _defaultCountry, required: !_addInternationalOption, countries: _countries || getCountries(_metadata), getAnyCountry: function getAnyCountry() { return _this.getFirstSupportedCountry({ countries: _countries }); }, metadata: _metadata }); _this.state = { // Workaround for `this.props` inside `getDerivedStateFromProps()`. props: _this.props, // The country selected. country: preSelectedCountry, // `countries` are stored in `this.state` because they're filtered. // For example, a developer might theoretically pass some unsupported // countries as part of the `countries` property, and because of that // the component uses `this.state.countries` (which are filtered) // instead of `this.props.countries` // (which could potentially contain unsupported countries). countries: _countries, // `phoneDigits` state property holds non-formatted user's input. // The reason is that there's no way of finding out // in which form should `value` be displayed: international or national. // E.g. if `value` is `+78005553535` then it could be input // by a user both as `8 (800) 555-35-35` and `+7 800 555 35 35`. // Hence storing just `value` is not sufficient for correct formatting. // E.g. if a user entered `8 (800) 555-35-35` // then value is `+78005553535` and `phoneDigits` are `88005553535` // and if a user entered `+7 800 555 35 35` // then value is `+78005553535` and `phoneDigits` are `+78005553535`. phoneDigits: getInitialPhoneDigits({ value: _value, phoneNumber: phoneNumber, defaultCountry: _defaultCountry, international: _international, useNationalFormat: displayInitialValueAsLocalNumber || initialValueFormat === 'national', metadata: _metadata }), // `value` property is duplicated in state. // The reason is that `getDerivedStateFromProps()` // needs this `value` to compare to the new `value` property // to find out if `phoneDigits` needs updating: // If the `value` property was changed externally // then it won't be equal to `state.value` // in which case `phoneDigits` and `country` should be updated. value: _value }; return _this; } _inherits(PhoneNumberInput_, _React$PureComponent); return _createClass(PhoneNumberInput_, [{ key: "componentDidMount", value: function componentDidMount() { var onCountryChange = this.props.onCountryChange; var defaultCountry = this.props.defaultCountry; var selectedCountry = this.state.country; if (onCountryChange) { if (defaultCountry) { if (!this.isCountrySupportedWithError(defaultCountry)) { defaultCountry = undefined; } } if (selectedCountry !== defaultCountry) { onCountryChange(selectedCountry); } } } }, { key: "componentDidUpdate", value: function componentDidUpdate(prevProps, prevState) { var onCountryChange = this.props.onCountryChange; var country = this.state.country; // Call `onCountryChange` when user selects another country. if (onCountryChange && country !== prevState.country) { onCountryChange(country); } } }, { key: "getCountrySelectOptions", value: function getCountrySelectOptions(_ref) { var countries = _ref.countries; var _this$props5 = this.props, international = _this$props5.international, countryCallingCodeEditable = _this$props5.countryCallingCodeEditable, countryOptionsOrder = _this$props5.countryOptionsOrder, addInternationalOption = _this$props5.addInternationalOption, labels = _this$props5.labels, locales = _this$props5.locales, metadata = _this$props5.metadata; return this.useMemoCountrySelectOptions(function () { return sortCountryOptions(_getCountrySelectOptions({ countries: countries || getCountries(metadata), countryNames: labels, addInternationalOption: international && countryCallingCodeEditable === false ? false : addInternationalOption, compareStringsLocales: locales // compareStrings }), getSupportedCountryOptions(countryOptionsOrder, metadata)); }, [countries, countryOptionsOrder, addInternationalOption, labels, metadata]); } }, { key: "useMemoCountrySelectOptions", value: function useMemoCountrySelectOptions(generator, dependencies) { if (!this.countrySelectOptionsMemoDependencies || !areEqualArrays(dependencies, this.countrySelectOptionsMemoDependencies)) { this.countrySelectOptionsMemo = generator(); this.countrySelectOptionsMemoDependencies = dependencies; } return this.countrySelectOptionsMemo; } }, { key: "getFirstSupportedCountry", value: function getFirstSupportedCountry(_ref2) { var countries = _ref2.countries; var countryOptions = this.getCountrySelectOptions({ countries: countries }); return countryOptions[0].value; } }, { key: "render", value: function render() { var _this$props6 = this.props, name = _this$props6.name, disabled = _this$props6.disabled, readOnly = _this$props6.readOnly, autoComplete = _this$props6.autoComplete, style = _this$props6.style, className = _this$props6.className, inputRef = _this$props6.inputRef, inputComponent = _this$props6.inputComponent, numberInputProps = _this$props6.numberInputProps, smartCaret = _this$props6.smartCaret, CountrySelectComponent = _this$props6.countrySelectComponent, countrySelectProps = _this$props6.countrySelectProps, ContainerComponent = _this$props6.containerComponent, containerComponentProps = _this$props6.containerComponentProps, defaultCountry = _this$props6.defaultCountry, countriesProperty = _this$props6.countries, countryOptionsOrder = _this$props6.countryOptionsOrder, labels = _this$props6.labels, flags = _this$props6.flags, flagComponent = _this$props6.flagComponent, flagUrl = _this$props6.flagUrl, addInternationalOption = _this$props6.addInternationalOption, internationalIcon = _this$props6.internationalIcon, displayInitialValueAsLocalNumber = _this$props6.displayInitialValueAsLocalNumber, initialValueFormat = _this$props6.initialValueFormat, onCountryChange = _this$props6.onCountryChange, limitMaxLength = _this$props6.limitMaxLength, countryCallingCodeEditable = _this$props6.countryCallingCodeEditable, focusInputOnCountrySelection = _this$props6.focusInputOnCountrySelection, reset = _this$props6.reset, metadata = _this$props6.metadata, international = _this$props6.international, locales = _this$props6.locales, rest = _objectWithoutProperties(_this$props6, _excluded); var _this$state3 = this.state, country = _this$state3.country, countries = _this$state3.countries, phoneDigits = _this$state3.phoneDigits, isFocused = _this$state3.isFocused; var InputComponent = smartCaret ? InputSmart : InputBasic; var countrySelectOptions = this.getCountrySelectOptions({ countries: countries }); return /*#__PURE__*/React.createElement(ContainerComponent, _extends({ style: style, className: classNames(className, 'PhoneInput', { 'PhoneInput--focus': isFocused, 'PhoneInput--disabled': disabled, 'PhoneInput--readOnly': readOnly }) }, containerComponentProps), /*#__PURE__*/React.createElement(CountrySelectComponent, _extends({ name: name ? "".concat(name, "Country") : undefined, "aria-label": labels.country }, countrySelectProps, { value: country, options: countrySelectOptions, onChange: this.onCountryChange, onFocus: this.onCountryFocus, onBlur: this.onCountryBlur, disabled: disabled || countrySelectProps && countrySelectProps.disabled, readOnly: readOnly || countrySelectProps && countrySelectProps.readOnly, iconComponent: this.CountryIcon })), /*#__PURE__*/React.createElement(InputComponent, _extends({ ref: this.setInputRef, type: "tel", autoComplete: autoComplete }, numberInputProps, rest, { inputFormat: international === true ? 'INTERNATIONAL' : international === false ? 'NATIONAL' : 'INTERNATIONAL_OR_NATIONAL', international: international ? true : undefined, withCountryCallingCode: international ? true : undefined, name: name, metadata: metadata, country: country, value: phoneDigits || '', onChange: this.onChange, onFocus: this.onFocus, onBlur: this.onBlur, disabled: disabled, readOnly: readOnly, inputComponent: inputComponent, className: classNames('PhoneInputInput', numberInputProps && numberInputProps.className, rest.className) }))); } }], [{ key: "getDerivedStateFromProps", value: // `state` holds previous props as `props`, and also: // * `country` — The currently selected country, e.g. `"RU"`. // * `value` — The currently entered phone number (E.164), e.g. `+78005553535`. // * `phoneDigits` — The parsed `<input/>` value, e.g. `8005553535`. // (and a couple of other less significant properties) function getDerivedStateFromProps(props, state) { return _objectSpread({ // Emulate `prevProps` via `state.props`. props: props }, getPhoneInputWithCountryStateUpdateFromNewProps(props, state.props, state)); } }]); }(React.PureComponent); // This wrapper is only to `.forwardRef()` to the `<input/>`. var PhoneNumberInput = /*#__PURE__*/React.forwardRef(function (props, ref) { return /*#__PURE__*/React.createElement(PhoneNumberInput_, _extends({}, withDefaultProps(props), { inputRef: ref })); }); PhoneNumberInput.propTypes = { /** * Phone number in `E.164` format. * * Example: * * `"+12223333333"` * * Any "falsy" value like `undefined`, `null` or an empty string `""` is treated like "empty". */ value: PropTypes.string, /** * A function of `value: string?`. * * Updates the `value` property as the user inputs a phone number. * * If the user erases the input value, the argument is `undefined`. */ onChange: PropTypes.func.isRequired, /** * Toggles the `--focus` CSS class. * @ignore */ onFocus: PropTypes.func, /** * `onBlur` is usually passed by `redux-form`. * @ignore */ onBlur: PropTypes.func, /** * Set to `true` to mark both the phone number `<input/>` * and the country `<select/>` as `disabled`. */ disabled: PropTypes.bool, /** * Set to `true` to mark both the phone number `<input/>` * and the country `<select/>` as `readonly`. */ readOnly: PropTypes.bool, /** * Sets `autoComplete` property for phone number `<input/>`. * * Web browser's "autocomplete" feature * remembers the phone number being input * and can also autofill the `<input/>` * with previously remembered phone numbers. * * https://developers.google.com * /web/updates/2015/06/checkout-faster-with-autofill * * For example, can be used to turn it off: * * "So when should you use `autocomplete="off"`? * One example is when you've implemented your own version * of autocomplete for search. Another example is any form field * where users will input and submit different kinds of information * where it would not be useful to have the browser remember * what was submitted previously". */ // (is `"tel"` by default) autoComplete: PropTypes.string, /** * Set to `"national"` to show the initial `value` in * "national" format rather than "international". * * For example, if `initialValueFormat` is `"national"` * and the initial `value="+12133734253"` is passed * then the `<input/>` value will be `"(213) 373-4253"`. * * By default, `initialValueFormat` is `undefined`, * meaning that if the initial `value="+12133734253"` is passed * then the `<input/>` value will be `"+1 213 373 4253"`. * * The reason for such default behaviour is that * the newer generation grows up when there are no stationary phones * and therefore everyone inputs phone numbers in international format * in their smartphones so people gradually get more accustomed to * writing phone numbers in international format rather than in local format. * Future people won't be using "national" format, only "international". */ // (is `undefined` by default) initialValueFormat: PropTypes.oneOf(['national']), // `displayInitialValueAsLocalNumber` property has been // superceded by `initialValueFormat` property. displayInitialValueAsLocalNumber: PropTypes.bool, /** * The country to be selected by default. * For example, can be set after a GeoIP lookup. * * Example: `"US"`. */ // A two-letter country code ("ISO 3166-1 alpha-2"). defaultCountry: PropTypes.string, /** * If specified, only these countries will be available for selection. * * Example: * * `["RU", "UA", "KZ"]` */ countries: PropTypes.arrayOf(PropTypes.string), /** * Custom country `<select/>` option names. * Also some labels like "ext" and country `<select/>` `aria-label`. * * Example: * * `{ "ZZ": "Международный", RU: "Россия", US: "США", ... }` * * See the `locales` directory for examples. */ labels: labelsPropType, /** * Country `<select/>` options are sorted by their labels. * The default sorting function uses `a.localeCompare(b, locales)`, * and, if that's not available, falls back to simple `a > b` / `a < b`. * Some languages, like Chinese, support multiple sorting variants * (called "collations"), and the user might prefer one or another. * Also, sometimes the Operating System language is not always * the preferred language for a person using a website or an application, * so there should be a way to specify custom locale. * This `locales` property mimicks the `locales` argument of `Intl` constructors, * and can be either a Unicode BCP 47 locale identifier or an array of such locale identifiers. * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locales_argument */ locales: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]), /* * Custom country `<select/>` options sorting function. * The default one uses `a.localeCompare(b)`, and, * if that's not available, falls back to simple `a > b`/`a < b`. * There have been requests to add custom sorter for cases * like Chinese language and "pinyin" (non-default) sorting order. * https://stackoverflow.com/questions/22907288/chinese-sorting-by-pinyin-in-javascript-with-localecompare compareStrings: PropTypes.func, */ /** * A URL template of a country flag, where * "{XX}" is a two-letter country code in upper case, * or where "{xx}" is a two-letter country code in lower case. * By default it points to `country-flag-icons` gitlab pages website. * I imagine someone might want to download those country flag icons * and host them on their own servers instead * (all flags are available in the `country-flag-icons` library). * There's a catch though: new countries may be added in future, * so when hosting country flag icons on your own server * one should check the `CHANGELOG.md` every time before updating this library, * otherwise there's a possibility that some new country flag would be missing. */ flagUrl: PropTypes.string, /** * Custom country flag icon components. * These flags will be used instead of the default ones. * The the "Flags" section of the readme for more info. * * The shape is an object where keys are country codes * and values are flag icon components. * Flag icon components receive the same properties * as `flagComponent` (see below). * * Example: * * `{ "RU": (props) => <img src="..."/> }` * * Example: * * `import flags from 'country-flag-icons/react/3x2'` * * `import PhoneInput from 'react-phone-number-input'` * * `<PhoneInput flags={flags} .../>` */ flags: PropTypes.objectOf(PropTypes.elementType), /** * Country flag icon component. * * Takes properties: * * * `country: string` — The country code. * * `countryName: string` — The country name. * * `flagUrl: string` — The `flagUrl` property (see above). * * `flags: object` — The `flags` property (see above). */ flagComponent: PropTypes.elementType, /** * Set to `false` to remove the "International" option from country `<select/>`. */ addInternationalOption: PropTypes.bool, /** * "International" icon component. * Should have the same aspect ratio. * * Receives properties: * * * `title: string` — "International" country option label. */ internationalIcon: PropTypes.elementType, /** * Can be used to place some countries on top of the list of country `<select/>` options. * * * `"XX"` — inserts an option for "XX" country. * * `"🌐"` — inserts "International" option. * * `"|"` — inserts a separator. * * `"..."` — inserts options for the rest of the countries (can be omitted, in which case it will be automatically added at the end). * * Example: * * `["US", "CA", "AU", "|", "..."]` */ countryOptionsOrder: PropTypes.arrayOf(PropTypes.string), /** * `<Phone/>` component CSS style object. */ style: PropTypes.object, /** * `<Phone/>` component CSS class. */ className: PropTypes.string, /** * Country `<select/>` component. * * Receives properties: * * * `name: string?` — HTML `name` attribute. * * `value: string?` — The currently selected country code. * * `onChange(value: string?)` — Updates the `value`. * * `onFocus()` — Is used to toggle the `--focus` CSS class. * * `onBlur()` — Is used to toggle the `--focus` CSS class. * * `options: object[]` — The list of all selectable countries (including "International") each being an object of shape `{ value: string?, label: string }`. * * `iconComponent: PropTypes.elementType` — React component that renders a country icon: `<Icon country={value}/>`. If `country` is `undefined` then it renders an "International" icon. * * `disabled: boolean?` — HTML `disabled` attribute. * * `readOnly: boolean?` — HTML `readOnly` attribute. * * `tabIndex: (number|string)?` — HTML `tabIndex` attribute. * * `className: string` — CSS class name. */ countrySelectComponent: PropTypes.elementType, /** * Country `<select/>` component props. * Along with the usual DOM properties such as `aria-label` and `tabIndex`, * some custom properties are supported, such as `arrowComponent` and `unicodeFlags`. */ countrySelectProps: PropTypes.object, /** * Phone number `<input/>` component. * * Receives properties: * * * `value: string` — The formatted `value`. * * `onChange(event: Event)` — Updates the formatted `value` from `event.target.value`. * * `onFocus()` — Is used to toggle the `--focus` CSS class. * * `onBlur()` — Is used to toggle the `--focus` CSS class. * * Other properties like `type="tel"` or `autoComplete="tel"` that should be passed through to the DOM `<input/>`. * * Must also either use `React.forwardRef()` to "forward" `ref` to the `<input/>` or implement `.focus()` method. */ inputComponent: PropTypes.elementType, /** * Phone number `<input/>` component props. */ numberInputProps: PropTypes.object, /** * Wrapping `<div/>` component. * * Receives properties: * * * `style: object` — A component CSS style object. * * `className: string` — Classes to attach to the component, typically changes when component focuses or blurs. */ containerComponent: PropTypes.elementType, /** * Wrapping `<div/>` component props. */ containerComponentProps: PropTypes.object, /** * When the user attempts to insert a digit somewhere in the middle of a phone number, * the caret position is moved right before the next available digit skipping * any punctuation in between. This is called "smart" caret positioning. * Another case would be the phone number format changing as a result of * the user inserting the digit somewhere in the middle, which would require * re-positioning the caret because all digit positions have changed. * This "smart" caret positioning feature can be turned off by passing * `smartCaret={false}` property: use it in case of any possible issues * with caret position during phone number input. */ // Is `true` by default. smartCaret: PropTypes.bool, /** * Set to `true` to force "international" phone number format. * Set to `false` to force "national" phone number format. * By default it's `undefined` meaning that it doesn't enforce any phone number format: * the user can input their phone number in either "national" or "international" format. */ international: PropTypes.bool, /** * If set to `true`, the phone number input will get trimmed * if it exceeds the maximum length for the country. */ limitMaxLength: PropTypes.bool, /** * If set to `false`, and `international` is `true`, then * users won't be able to erase the "country calling part" * of a phone number in the `<input/>`. */ countryCallingCodeEditable: PropTypes.bool, /** * `libphonenumber-js` metadata. * * Can be used to pass custom `libphonenumber-js` metadata * to reduce the overall bundle size for those who compile "custom" metadata. */ metadata: metadataPropType, /** * Is called every time the selected country changes: * either programmatically or when user selects it manually from the list. */ // People have been asking for a way to get the selected country. // @see https://github.com/catamphetamine/react-phone-number-input/issues/128 // For some it's just a "business requirement". // I guess it's about gathering as much info on the user as a website can // without introducing any addional fields that would complicate the form // therefore reducing "conversion" (that's a marketing term). // Assuming that the phone number's country is the user's country // is not 100% correct but in most cases I guess it's valid. onCountryChange: PropTypes.func, /** * If set to `false`, will not focus the `<input/>` component * when the user selects a country from the list of countries. * This can be used to conform to the Web Content Accessibility Guidelines (WCAG). * Quote: * "On input: Changing the setting of any user interface component * does not automatically cause a change of context unless the user * has been advised of the behaviour before using the component." */ focusInputOnCountrySelection: PropTypes.bool }; var defaultProps = { /** * Remember (and autofill) the value as a phone number. */ autoComplete: 'tel', /** * Country `<select/>` component. */ countrySelectComponent: CountrySelect, /** * Flag icon component. */ flagComponent: Flag, /** * By default, uses icons from `country-flag-icons` gitlab pages website. */ // Must be equal to `flagUrl` in `./CountryIcon.js`. flagUrl: 'https://purecatamphetamine.github.io/country-flag-icons/3x2/{XX}.svg', /** * Default "International" country `<select/>` option icon. */ internationalIcon: InternationalIcon, /** * Phone number `<input/>` component. */ inputComponent: 'input', /** * Wrapping `<div/>` component. */ containerComponent: 'div', /** * Some users requested a way to reset the component: * both number `<input/>` and country `<select/>`. * Whenever `reset` property changes both number `<input/>` * and country `<select/>` are reset. * It's not implemented as some instance `.reset()` method * because `ref` is forwarded to `<input/>`. * It's also not replaced with just resetting `country` on * external `value` reset, because a user could select a country * and then not input any `value`, and so the selected country * would be "stuck", if not using this `reset` property. */ // https://github.com/catamphetamine/react-phone-number-input/issues/300 reset: PropTypes.any, /** * */ /** * Set to `false` to use "basic" caret instead of the "smart" one. */ smartCaret: true, /** * Whether to add the "International" option * to the list of countries. */ addInternationalOption: true, /** * If set to `false`, and `international` is `true`, then * users won't be able to erase the "country calling part" * of a phone number in the `<input/>`. */ countryCallingCodeEditable: true, /** * If set to `false`, will not focus the `<input/>` component * when the user selects a country from the list of countries. * This can be used to conform to the Web Content Accessibility Guidelines (WCAG). * Quote: * "On input: Changing the setting of any user interface component * does not automatically cause a change of context unless the user * has been advised of the behaviour before using the component." */ focusInputOnCountrySelection: true }; function withDefaultProps(props) { props = _objectSpread({}, props); for (var key in defaultProps) { if (props[key] === undefined) { props[key] = defaultProps[key]; } } return props; } export default PhoneNumberInput; function areEqualArrays(a, b) { if (a.length !== b.length) { return false; } var i = 0; while (i < a.length) { if (a[i] !== b[i]) { return false; } i++; } return true; } //# sourceMappingURL=PhoneInputWithCountry.js.map