UNPKG

react-phone-number-input

Version:
1,161 lines (981 loc) 39.7 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = undefined; var _extends = Object.assign || 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; }; var _createClass = 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, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _class, _class2, _temp, _initialiseProps; // import InputSmart from './InputSmart' var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); var _reactLifecyclesCompat = require('react-lifecycles-compat'); var _core = require('libphonenumber-js/core'); var _InputBasic = require('./InputBasic'); var _InputBasic2 = _interopRequireDefault(_InputBasic); var _Flag = require('./Flag'); var _Flag2 = _interopRequireDefault(_Flag); var _PropTypes = require('./PropTypes'); var _inputControl = require('./input-control'); var _countries2 = require('./countries'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // `PureComponent` is only available in React >= 15.3.0. var PureComponent = _react2.default.PureComponent || _react2.default.Component; var PhoneNumberInput = (0, _reactLifecyclesCompat.polyfill)(_class = (_temp = _class2 = function (_PureComponent) { _inherits(PhoneNumberInput, _PureComponent); function PhoneNumberInput(props) { _classCallCheck(this, PhoneNumberInput); var _this = _possibleConstructorReturn(this, (PhoneNumberInput.__proto__ || Object.getPrototypeOf(PhoneNumberInput)).call(this, props)); _initialiseProps.call(_this); var _this$props = _this.props, value = _this$props.value, labels = _this$props.labels, international = _this$props.international, metadata = _this$props.metadata; var _this$props2 = _this.props, country = _this$props2.country, countries = _this$props2.countries, countryOptions = _this$props2.countryOptions; // Validate `country`. if (country) { if (!_this.isCountrySupportedWithError(country)) { country = undefined; } } // Validate `countries`. countries = filterCountries(countries, metadata); // Validate `countryOptions`. countryOptions = filterCountryOptions(countryOptions, metadata); var phoneNumber = (0, _inputControl.parsePhoneNumber)(value, metadata); var pre_selected_country = (0, _inputControl.getPreSelectedCountry)(phoneNumber, country, countries || (0, _countries2.getCountryCodes)(labels).filter(function (_) { return _ === 'ZZ' || metadata.countries[_]; }), international, metadata); _this.state = { // Workaround for `this.props` inside `getDerivedStateFromProps()`. props: _this.props, // The country selected. country: pre_selected_country, // `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, // Generate country `<select/>` options. country_select_options: generateCountrySelectOptions(countries, countryOptions, _this.props), // `parsed_input` 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 `parsed_input` is `88005553535` // and if a user entered `+7 800 555 35 35` // then value is `+78005553535` and `parsed_input` is `+78005553535`. parsed_input: generateParsedInput(value, phoneNumber, _this.props), // `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 `parsed_input` needs updating: // If the `value` property was changed externally // then it won't be equal to `state.value` // in which case `parsed_input` and `country` should be updated. value: value }; return _this; } _createClass(PhoneNumberInput, [{ key: 'componentDidMount', value: function componentDidMount() { var onCountryChange = this.props.onCountryChange; var country = this.props.country; var selectedCountry = this.state.country; if (onCountryChange) { if (!country || !this.isCountrySupportedWithError(country)) { country = undefined; } if (selectedCountry !== country) { onCountryChange(selectedCountry); } } } }, { key: 'componentDidUpdate', value: function componentDidUpdate(prevProps, prevState) { var onCountryChange = this.props.onCountryChange; // Call `onCountryChange` when user selects another country. if (onCountryChange && this.state.country !== prevState.country) { onCountryChange(this.state.country); } } // A shorthand for not passing `metadata` as a second argument. // Country `<select/>` `onChange` handler. // Phone number `<input/>` `onKeyDown` handler. /** * `<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"`. */ // Toggles the `--focus` CSS class. // Toggles the `--focus` CSS class. // This `onBlur` interceptor is a workaround for `redux-form` // so that it gets the up-to-date `value` in its `onBlur` handler. // Without this fix it just gets the actual (raw) input field textual value. // E.g. `+7 800 555 35 35` instead of `+78005553535`. // // A developer is not supposed to pass this `onBlur` property manually. // Instead, `redux-form` passes `onBlur` to this component automatically // and this component patches that `onBlur` handler (a hacky way but works). // // When country `<select/>` is toggled. // Can be called externally. }, { key: 'render', value: function render() { var _props = this.props, name = _props.name, disabled = _props.disabled, disablePhoneInput = _props.disablePhoneInput, autoComplete = _props.autoComplete, countrySelectTabIndex = _props.countrySelectTabIndex, showCountrySelect = _props.showCountrySelect, style = _props.style, className = _props.className, inputClassName = _props.inputClassName, getInputClassName = _props.getInputClassName, countrySelectProperties = _props.countrySelectProperties, error = _props.error, indicateInvalid = _props.indicateInvalid, CountrySelectComponent = _props.countrySelectComponent, InputComponent = _props.inputComponent, ext = _props.ext, _ = _props.country, countries = _props.countries, countryOptions = _props.countryOptions, labels = _props.labels, flags = _props.flags, flagComponent = _props.flagComponent, flagsPath = _props.flagsPath, international = _props.international, internationalIcon = _props.internationalIcon, displayInitialValueAsLocalNumber = _props.displayInitialValueAsLocalNumber, onCountryChange = _props.onCountryChange, limitMaxLength = _props.limitMaxLength, metadata = _props.metadata, phoneNumberInputProps = _objectWithoutProperties(_props, ['name', 'disabled', 'disablePhoneInput', 'autoComplete', 'countrySelectTabIndex', 'showCountrySelect', 'style', 'className', 'inputClassName', 'getInputClassName', 'countrySelectProperties', 'error', 'indicateInvalid', 'countrySelectComponent', 'inputComponent', 'ext', 'country', 'countries', 'countryOptions', 'labels', 'flags', 'flagComponent', 'flagsPath', 'international', 'internationalIcon', 'displayInitialValueAsLocalNumber', 'onCountryChange', 'limitMaxLength', 'metadata']); var _state = this.state, country = _state.country, hidePhoneInputField = _state.hidePhoneInputField, country_select_options = _state.country_select_options, parsed_input = _state.parsed_input, isFocused = _state.isFocused; // const InputComponent = inputComponent || (smartCaret ? InputSmart : InputBasic) // Extract `countrySelectProperties` from `this.props` // also removing them from `phoneNumberInputProps`. var _countrySelectProps = {}; if (countrySelectProperties) { for (var key in countrySelectProperties) { if (this.props.hasOwnProperty(key)) { _countrySelectProps[countrySelectProperties[key]] = this.props[key]; delete phoneNumberInputProps[key]; } } } return _react2.default.createElement( 'div', { style: style, className: (0, _classnames2.default)('react-phone-number-input', { 'react-phone-number-input--focus': isFocused, 'react-phone-number-input--invalid': error && indicateInvalid }, className) }, _react2.default.createElement( 'div', { className: 'react-phone-number-input__row' }, showCountrySelect && _react2.default.createElement(CountrySelectComponent, _extends({}, _countrySelectProps, { ref: this.storeCountrySelectInstance, name: name ? name + '__country' : undefined, value: country, options: country_select_options, onChange: this.onCountryChange, onFocus: this._onFocus, onBlur: this._onBlur, disabled: disabled, tabIndex: countrySelectTabIndex, hidePhoneInputField: this.hidePhoneInputField, focusPhoneInputField: this.focus, 'aria-label': labels.country, className: 'react-phone-number-input__country' })), !hidePhoneInputField && _react2.default.createElement(InputComponent, _extends({ type: 'tel', name: name }, phoneNumberInputProps, { ref: this.storePhoneNumberInputInstance, metadata: metadata, country: country, value: parsed_input || '', onChange: this.onChange, onFocus: this.onFocus, onBlur: this.onBlur, onKeyDown: this.onPhoneNumberKeyDown, disabled: disabled || disablePhoneInput, autoComplete: autoComplete, className: (0, _classnames2.default)('react-phone-number-input__input', 'react-phone-number-input__phone', { 'react-phone-number-input__input--disabled': disabled || disablePhoneInput, 'react-phone-number-input__input--invalid': error && indicateInvalid }, inputClassName, getInputClassName && getInputClassName({ disabled: disabled || disablePhoneInput, invalid: error && indicateInvalid })) })), ext && !hidePhoneInputField && _react2.default.createElement( 'label', { className: 'react-phone-number-input__ext' }, labels.ext, _react2.default.cloneElement(ext, { onChange: ext.props.onChange ? function (event) { return ext.props.onChange(parseExtDigits(event)); } : undefined, onFocus: this._onFocus, onBlur: this._onBlur, className: (0, _classnames2.default)('react-phone-number-input__input', 'react-phone-number-input__ext-input', { 'react-phone-number-input__input--disabled': disabled || disablePhoneInput }, inputClassName, getInputClassName && getInputClassName({ disabled: disabled || disablePhoneInput }), ext.props.className) }) ) ), error && indicateInvalid && _react2.default.createElement( 'div', { className: 'react-phone-number-input__error' }, error ) ); } }], [{ key: 'getDerivedStateFromProps', // `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`. // * `parsed_input` — The parsed `<input/>` value, e.g. `8005553535`. // (and a couple of other less significant properties) value: function getDerivedStateFromProps(props, state) { var country = state.country, hasUserSelectedACountry = state.hasUserSelectedACountry, value = state.value, _state$props = state.props, old_default_country = _state$props.country, old_value = _state$props.value; var metadata = props.metadata, countries = props.countries, new_default_country = props.country, new_value = props.value; var new_state = { // Emulate `prevProps` via `state.props`. props: props, // If the user has already manually selected a country // then don't override that already selected country // if the default `country` property changes. // That's what `hasUserSelectedACountry` flag is for. hasUserSelectedACountry: hasUserSelectedACountry // If `countries` or `labels` or `international` changed // then re-generate country `<select/>` options. };if (props.countries !== state.props.countries || props.labels !== state.props.labels || props.international !== state.props.international) { // Re-generate country select options. new_state.country_select_options = generateCountrySelectOptions(filterCountries(props.countries, metadata), filterCountryOptions(props.countryOptions, metadata), props); } // If the default country changed. // (e.g. in case of ajax GeoIP detection after page loaded) // then select it but only if the user hasn't already manually // selected a country and no phone number has been entered so far. // Because if the user has already started inputting a phone number // then he's okay with no country being selected at all ("International") // and doesn't want to be disturbed, doesn't want his input to be screwed, etc. if (new_default_country !== old_default_country && !hasUserSelectedACountry && !value && !new_value) { return _extends({}, new_state, { country: isCountrySupportedWithError(new_default_country, metadata) ? new_default_country : old_default_country // `value` is `undefined`. // `parsed_input` is `undefined` because `value` is `undefined`. }); } // If a new `value` is set externally. // (e.g. as a result of an ajax API request // to get user's phone after page loaded) // The first part — `new_value !== old_value` — // is basically `props.value !== prevProps.value` // so it means "if value property was changed externally". // The second part — `new_value !== value` — // is for ignoring the `getDerivedStateFromProps()` call // which happens in `this.onChange()` right after `this.setState()`. // If this `getDerivedStateFromProps()` call isn't ignored // then the country flag would reset on each input. else if (new_value !== old_value && new_value !== value) { var phoneNumber = (0, _inputControl.parsePhoneNumber)(new_value, metadata); var parsedCountry = void 0; if (phoneNumber) { var _countries = filterCountries(props.countries, metadata); if (!_countries || _countries.indexOf(phoneNumber.country) >= 0) { parsedCountry = phoneNumber.country; } } return _extends({}, new_state, { parsed_input: generateParsedInput(new_value, phoneNumber, props), value: new_value, country: new_value ? parsedCountry : country }); } // `country` didn't change. // `value` didn't change. // `parsed_input` didn't change, because `value` didn't change. // // Maybe `new_state.country_select_options` changed. // In any case, update `prevProps`. return new_state; } }]); return PhoneNumberInput; }(PureComponent), _class2.propTypes = { /** * Phone number in `E.164` format. * * Example: * * `"+12223333333"` */ value: _propTypes2.default.string, /** * Updates the `value` as the user inputs the phone number. */ onChange: _propTypes2.default.func.isRequired, /** * Toggles the `--focus` CSS class. * @ignore */ onFocus: _propTypes2.default.func, /** * `onBlur` is usually passed by `redux-form`. * @ignore */ onBlur: _propTypes2.default.func, /** * `onKeyDown` handler (e.g. to handle Enter key press). * @ignore */ onKeyDown: _propTypes2.default.func, /** * Disables both the phone number `<input/>` * and the country `<select/>`. */ // (is `false` by default) disabled: _propTypes2.default.bool.isRequired, /** * 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: _propTypes2.default.string.isRequired, /** * Set to `true` to show the initial `value` in * "national" format rather than "international". * * For example, if this flag is set to `true` * and the initial `value="+12133734253"` is passed * then the `<input/>` value will be `"(213) 373-4253"`. * * By default, this flag is set to `false`, * 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 `false` by default) displayInitialValueAsLocalNumber: _propTypes2.default.bool.isRequired, /** * 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"). country: _propTypes2.default.string, /** * If specified, only these countries will be available for selection. * * Example: * * `["RU", "UA", "KZ"]` */ countries: _propTypes2.default.arrayOf(_propTypes2.default.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: _PropTypes.labels.isRequired, /** * The base URL path for country flag icons. * By default it loads country flag icons from * `flag-icon-css` repo github pages website. * I imagine someone might want to download * those country flag icons and host them * on their own servers instead. * Warning: in future new countries can be added * to the country select which would result in * "Image not found" errors when using custom `flagsPath` * due to the custom-hosted flags bundle being outdated * and missing the new flags. * So if using custom `flagsPath` always check `CHANGELOG.md` * for new country announcements before updating this library. */ flagsPath: _propTypes2.default.string.isRequired, /** * Custom country flag icon components. * These flags replace the default ones. * * 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": () => <img src="..."/> }` * * Can be used to replace the default flags * with custom ones for certain (or all) countries. * * Can also be used to bundle `<svg/>` flags instead of `<img/>`s: * * By default flag icons are inserted as `<img/>`s * with their `src` pointed to `flag-icon-css` repo github pages website. * * There might be some cases * (e.g. a standalone "native" app, or an "intranet" web application) * when including the full set of `<svg/>` country flags (3 megabytes) * is more appropriate than downloading them individually at runtime only if needed. * * Example: * * `// Uses <svg/> flags (3 megabytes):` * * `import flags from 'react-phone-number-input/flags'` * * `import PhoneInput from 'react-phone-number-input'` * * `<PhoneInput flags={flags} .../>` */ flags: _propTypes2.default.objectOf(_propTypes2.default.elementType), /** * Country flag icon component. * * Takes properties: * * * country : string — The country code. * * flagsPath : string — The `flagsPath` property (see above). * * flags : object — The `flags` property (see above). */ flagComponent: _propTypes2.default.elementType.isRequired, /** * Set to `false` to drop the "International" option from country `<select/>`. */ international: _propTypes2.default.bool.isRequired, /** * Custom "International" country `<select/>` option icon. */ internationalIcon: _propTypes2.default.elementType.isRequired, /** * Set to `false` to hide country `<select/>`. */ // (is `true` by default) showCountrySelect: _propTypes2.default.bool.isRequired, /** * HTML `tabindex` attribute for country `<select/>`. */ countrySelectTabIndex: _propTypes2.default.number, /** * Can be used to place some countries on top of the list of country `<select/>` options. * * * `"|"` — inserts a separator. * * `"..."` — means "the rest of the countries" (can be omitted). * * Example: * * `["US", "CA", "AU", "|", "..."]` */ countryOptions: _propTypes2.default.arrayOf(_propTypes2.default.string), /** * `<Phone/>` component CSS style object. */ style: _propTypes2.default.object, /** * `<Phone/>` component CSS class. */ className: _propTypes2.default.string, /** * Phone number `<input/>` CSS class. */ inputClassName: _propTypes2.default.string, /** * Returns phone number `<input/>` CSS class string. * Receives an object of shape `{ disabled : boolean?, invalid : boolean? }`. * @ignore */ getInputClassName: _propTypes2.default.func, /** * 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, icon : React.Component }`. * * `disabled : boolean?` — HTML `disabled` attribute. * * `tabIndex : (number|string)?` — HTML `tabIndex` attribute. * * `className : string` — CSS class name. */ // // (deprecated) // * `hidePhoneInputField(hide : boolean)` — Can be called to show/hide phone input field. Takes `hide : boolean` argument. E.g. `react-responsive-ui` `<Select/>` uses this to hide phone number input when country select is expanded. // * `focusPhoneInputField()` — Can be called to manually focus phone input field. E.g. `react-responsive-ui` `<Select/>` uses this to focus phone number input after country selection in a timeout (after the phone input field is no longer hidden). // countrySelectComponent: _propTypes2.default.elementType.isRequired, /** * Phone number `<input/>` component. * * Receives properties: * * * `value : string` — The parsed phone number. E.g.: `""`, `"+"`, `"+123"`, `"123"`. * * `onChange(value? : string)` — Updates the `value`. * * `onFocus()` — Is used to toggle the `--focus` CSS class. * * `onBlur()` — Is used to toggle the `--focus` CSS class. * * `country : string?` — The currently selected country. `undefined` means "International" (no country selected). * * `metadata : object` — `libphonenumber-js` metadata. * * All other properties should be passed through to the underlying `<input/>`. * * Must also implement `.focus()` method. */ inputComponent: _propTypes2.default.elementType.isRequired, /** * Set to `false` to use `inputComponent={InputBasic}` * instead of `input-format`'s `<ReactInput/>`. * Is `false` by default. */ // smartCaret : PropTypes.bool.isRequired, /** * Phone number extension `<input/>` element. * * Example: * * `ext={<input value={...} onChange={...}/>}` */ ext: _propTypes2.default.node, /** * If set to `true` the phone number input will get trimmed * if it exceeds the maximum length for the country. */ limitMaxLength: _propTypes2.default.bool.isRequired, /** * An error message to show below the phone number `<input/>`. For example, `"Required"`. */ error: _propTypes2.default.string, /** * The `error` is shown only when `indicateInvalid` is `true`. * (which is the default). * @deprecated * @ignore */ indicateInvalid: _propTypes2.default.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: _PropTypes.metadata.isRequired, /** * A long time ago a person requested an `onCountryChange(country)` event listener. * No valid reason was given other than compliance with some legacy code * which stored both phone number and country in a database. * @see https://github.com/catamphetamine/react-phone-number-input/issues/128 */ onCountryChange: _propTypes2.default.func, /** * Disables only the phone number `<input/>`. * * Some users choose to implement a digital keyboard component for phone number input. * In such cases the phone number input field must be disabled in order for the default system keyboard to not show up on focus. * At the same time, country select should not be disabled in order for the user to be able to choose their country. */ // (is `false` by default) // https://github.com/catamphetamine/react-phone-number-input/issues/215 disablePhoneInput: _propTypes2.default.bool.isRequired }, _class2.defaultProps = { /** * Not disabled. */ disabled: false, disablePhoneInput: false, /** * Show `error` (if passed). * @deprecated */ indicateInvalid: true, /** * Remember (and autofill) the value as a phone number. */ autoComplete: 'tel', /** * Flag icon component. */ flagComponent: _Flag2.default, /** * By default use icons from `flag-icon-css` github repo. */ flagsPath: 'https://lipis.github.io/flag-icon-css/flags/4x3/', /** * Default "International" country `<select/>` option icon (globe). */ // internationalIcon: InternationalIcon, /** * Phone number `<input/>` component. */ inputComponent: _InputBasic2.default, /** * Show country `<select/>`. */ showCountrySelect: true, /** * Don't convert the initially passed phone number `value` * to a national phone number for its country. * The reason is that the newer generation grows up when * there are no stationary phones and therefore everyone inputs * phone numbers with a `+` in their smartphones * so phone numbers written in international form * are gradually being considered more natural than local ones. */ displayInitialValueAsLocalNumber: false, /** * Set to `false` to use `inputComponent={InputBasic}` * instead of `input-format`'s `<ReactInput/>`. * Is `false` by default. */ // smartCaret : false, /** * Whether to add the "International" option * to the list of countries. */ international: true, /** * If set to `true` the phone number input will get trimmed * if it exceeds the maximum length for the country. */ limitMaxLength: false }, _initialiseProps = function _initialiseProps() { var _this2 = this; this.isCountrySupportedWithError = function (country) { var metadata = _this2.props.metadata; return isCountrySupportedWithError(country, metadata); }; this.onCountryChange = function (new_country) { var _props2 = _this2.props, metadata = _props2.metadata, onChange = _props2.onChange, displayInitialValueAsLocalNumber = _props2.displayInitialValueAsLocalNumber; var _state2 = _this2.state, old_parsed_input = _state2.parsed_input, old_country = _state2.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 new_parsed_input = (0, _inputControl.migrateParsedInputForNewCountry)(old_parsed_input, old_country, new_country, metadata, displayInitialValueAsLocalNumber); var new_value = (0, _inputControl.e164)(new_parsed_input, new_country, metadata); // Focus phone number `<input/>` upon country selection. _this2.focus(); // If the user has already manually selected a country // then don't override that already selected country // if the default `country` property changes. // That's what `hasUserSelectedACountry` flag is for. _this2.setState({ country: new_country, hasUserSelectedACountry: true, parsed_input: new_parsed_input, value: new_value }, 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(new_value); }); }; this.onPhoneNumberKeyDown = function (event) { var onKeyDown = _this2.props.onKeyDown; // Actually "Down arrow" key is used for showing "autocomplete" ("autofill") options. // (e.g. previously entered phone numbers for `autoComplete="tel"`) // so can't hijack "Down arrow" keypress here. // // Expand country `<select/>`` on "Down arrow" key press. // if (event.keyCode === 40) { // this.country_select.toggle() // } if (onKeyDown) { onKeyDown(event); } }; this.onChange = function (_input) { var _props3 = _this2.props, onChange = _props3.onChange, international = _props3.international, limitMaxLength = _props3.limitMaxLength, metadata = _props3.metadata; var _parseInput = (0, _inputControl.parseInput)(_input, _this2.state.country, _this2.state.countries, international, limitMaxLength, metadata), input = _parseInput.input, country = _parseInput.country, value = _parseInput.value; _this2.setState({ parsed_input: input, value: value, country: country }, // 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); }); }; this._onFocus = function () { return _this2.setState({ isFocused: true }); }; this._onBlur = function () { return _this2.setState({ isFocused: false }); }; this.onFocus = function (event) { var onFocus = _this2.props.onFocus; _this2._onFocus(); if (onFocus) { onFocus(event); } }; this.onBlur = function (event) { var onBlur = _this2.props.onBlur; var value = _this2.state.value; _this2._onBlur(); if (!onBlur) { return; } // `event` is React's `SyntheticEvent`. // Its `.value` is read-only therefore cloning it. var _event = _extends({}, event, { target: _extends({}, event.target, { value: value }) // For `redux-form` event detection. // https://github.com/erikras/redux-form/blob/v5/src/events/isEvent.js });_event.stopPropagation = event.stopPropagation; _event.preventDefault = event.preventDefault; return onBlur(_event); }; this.hidePhoneInputField = function (hide) { _this2.setState({ hidePhoneInputField: hide }); }; this.focus = function () { return _this2.number_input.focus(); }; this.storeCountrySelectInstance = function (_) { return _this2.country_select = _; }; this.storePhoneNumberInputInstance = function (_) { return _this2.number_input = _; }; }, _temp)) || _class; // Generates country `<select/>` options. exports.default = PhoneNumberInput; function generateCountrySelectOptions(countries, countryOptions, props) { var labels = props.labels, international = props.international, metadata = props.metadata; var CountrySelectOptionIcon = createCountrySelectOptionIconComponent(props); return transformCountryOptions((0, _inputControl.getCountrySelectOptions)(countries || (0, _countries2.getCountryCodes)(labels).filter(function (country) { return country === 'ZZ' || isCountrySupported(country, metadata); }), labels, international).map(function (_ref) { var value = _ref.value, label = _ref.label; return { value: value, label: label, icon: CountrySelectOptionIcon }; }), countryOptions); } function createCountrySelectOptionIconComponent(props) { var flags = props.flags, flagsPath = props.flagsPath, FlagComponent = props.flagComponent, InternationalIcon = props.internationalIcon; return function (_ref2) { var value = _ref2.value; return _react2.default.createElement( 'div', { className: (0, _classnames2.default)('react-phone-number-input__icon', { 'react-phone-number-input__icon--international': value === undefined }) }, value ? _react2.default.createElement(FlagComponent, { country: value, flags: flags, flagsPath: flagsPath }) : _react2.default.createElement(InternationalIcon, null) ); }; } // Can move some country `<select/>` options // to the top of the list, for example. // See `countryOptions` property. function transformCountryOptions(options, transform) { if (!transform) { return options; } var optionsOnTop = []; var optionsOnBottom = []; var appendTo = optionsOnTop; var _loop = function _loop() { if (_isArray) { if (_i >= _iterator.length) return 'break'; _ref3 = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) return 'break'; _ref3 = _i.value; } var element = _ref3; if (element === '|') { appendTo.push({ divider: true }); } else if (element === '...' || element === '…') { appendTo = optionsOnBottom; } else { // Find the position of the option. var index = options.indexOf(options.filter(function (option) { return option.value === element; })[0]); // Get the option. var option = options[index]; // Remove the option from its default position. options.splice(index, 1); // Add the option on top. appendTo.push(option); } }; for (var _iterator = transform, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref3; var _ret = _loop(); if (_ret === 'break') break; } return optionsOnTop.concat(options).concat(optionsOnBottom); } function generateParsedInput(value, phoneNumber, props) { var displayInitialValueAsLocalNumber = props.displayInitialValueAsLocalNumber; // If the `value` (E.164 phone number) // belongs to the currently selected country // and `displayInitialValueAsLocalNumber` property is `true` // then convert `value` (E.164 phone number) // to a local phone number digits. // E.g. '+78005553535' -> '88005553535'. if (displayInitialValueAsLocalNumber && phoneNumber && phoneNumber.country) { return (0, _inputControl.generateNationalNumberDigits)(phoneNumber); } return value; } function isCountrySupported(country, metadata) { return metadata.countries.hasOwnProperty(country); } function isCountrySupportedWithError(country, metadata) { if (isCountrySupported(country, metadata)) { return true; } else { console.error('Country not found: ' + country); return false; } } function isCountryOptionSupportedWithError(countryOption, metadata) { switch (countryOption) { case '|': case '...': case '…': return true; default: return isCountrySupportedWithError(countryOption, metadata); } } function filterCountries(countries, metadata) { if (countries) { countries = countries.filter(function (country) { return isCountrySupportedWithError(country, metadata); }); if (countries.length === 0) { countries = undefined; } } return countries; } function filterCountryOptions(countryOptions, metadata) { if (countryOptions) { countryOptions = countryOptions.filter(function (countryOption) { return isCountryOptionSupportedWithError(countryOption, metadata); }); if (countryOptions.length === 0) { countryOptions = undefined; } } return countryOptions; } function parseExtDigits(event) { if (event) { if (typeof event === 'string') { event = (0, _core.parseDigits)(event); } else if (event.target && event.target.value) { event.target.value = (0, _core.parseDigits)(event.target.value); } } return event; } //# sourceMappingURL=PhoneInput.js.map