├── flags ├── index.cjs ├── index.js ├── index.d.ts ├── index.cjs.js └── package.json ├── test ├── setup.js ├── exports.flags.test.js ├── exports.react-native-input.test.js ├── exports.max.test.js ├── exports.min.test.js ├── exports.input.test.js ├── exports.mobile.test.js ├── exports.core.test.js └── exports.input-core.test.js ├── react-hook-form-input-core ├── index.js ├── index.cjs ├── package.json ├── index.cjs.js └── index.d.ts ├── react-hook-form-core ├── index.js ├── index.cjs ├── package.json ├── index.cjs.js └── index.d.ts ├── docs └── images │ ├── first-glance.png │ ├── desktop-countries.png │ ├── first-glance-local.png │ ├── iphone-countries.png │ ├── iphone-native-select.png │ └── desktop-native-select.png ├── locale ├── ar.json.d.ts ├── ca.json.d.ts ├── cz.json.d.ts ├── de.json.d.ts ├── el.json.d.ts ├── en.json.d.ts ├── es.json.d.ts ├── et.json.d.ts ├── fi.json.d.ts ├── fr.json.d.ts ├── he.json.d.ts ├── hy.json.d.ts ├── it.json.d.ts ├── ja.json.d.ts ├── ko.json.d.ts ├── nb.json.d.ts ├── nl.json.d.ts ├── pl.json.d.ts ├── pt.json.d.ts ├── ru.json.d.ts ├── sk.json.d.ts ├── sv.json.d.ts ├── th.json.d.ts ├── tr.json.d.ts ├── ua.json.d.ts ├── vi.json.d.ts ├── zh.json.d.ts ├── pt-BR.json.d.ts ├── zh.json ├── ko.json ├── zh.json.js ├── ko.json.js └── ja.json ├── react-styleguidist ├── package.json ├── README.md └── styleguide.config.js ├── source ├── libphonenumber │ ├── README.md │ ├── formatPhoneNumber.js │ └── formatPhoneNumber.test.js ├── PropTypes.js ├── helpers │ ├── inputValuePrefix.js │ ├── getInternationalPhoneNumberPrefix.test.js │ ├── isE164Number.js │ ├── isE164Number.test.js │ ├── inputValuePrefix.test.js │ ├── parsePhoneNumberCharacter.test.js │ ├── getInternationalPhoneNumberPrefix.js │ ├── parsePhoneNumberCharacter.js │ ├── countries.js │ └── countries.test.js ├── react-hook-form │ ├── PhoneInput.js │ ├── PhoneInputWithCountry.js │ └── ReactHookFormInput.js ├── PhoneInputWithCountryDefault.js ├── react-native │ ├── PhoneInput.js │ └── PhoneTextInput.js ├── PhoneInputBrowser.js ├── Flag.js ├── useInputKeyDownHandler.js ├── CountryIcon.js ├── useExternalRef.js ├── InternationalIcon.js ├── InputSmart.js ├── CountrySelect.js ├── PhoneInput.js └── InputBasic.js ├── .travis.yml ├── react-native-input ├── index.js ├── index.cjs ├── package.json ├── index.cjs.js └── index.d.ts ├── react-hook-form-input ├── index.js ├── index.cjs ├── package.json ├── index.cjs.js └── index.d.ts ├── react-hook-form ├── index.js ├── index.cjs ├── package.json ├── index.cjs.js └── index.d.ts ├── max ├── index.d.ts ├── package.json ├── index.cjs ├── index.js └── index.cjs.js ├── min ├── index.d.ts ├── package.json ├── index.cjs ├── index.js └── index.cjs.js ├── mobile ├── index.d.ts ├── package.json ├── index.cjs ├── index.js └── index.cjs.js ├── input-max ├── index.d.ts ├── package.json ├── index.cjs ├── index.js └── index.cjs.js ├── input-mobile ├── index.d.ts ├── package.json ├── index.cjs ├── index.js └── index.cjs.js ├── .nycrc ├── core ├── package.json ├── index.js ├── index.cjs ├── index.d.ts └── index.cjs.js ├── input ├── package.json ├── index.cjs ├── index.js ├── index.cjs.js └── index.d.ts ├── input-core ├── package.json ├── index.js ├── index.cjs ├── index.d.ts └── index.cjs.js ├── scripts ├── create-commonjs-package-json.js ├── fix-locale-import-in-default-component.js ├── verify-flag-existence.js ├── build-bundle-styles.js ├── fix-locales.js └── generate-locale-exports.js ├── website ├── docs │ ├── index.html │ └── build │ │ └── bundle.js.LICENSE.txt └── lib │ └── prism.css ├── .gitattributes ├── .gitlab-ci.yml ├── .babelrc ├── project.sublime-project ├── .gitignore ├── react-styleguidist.cjs ├── icons ├── international-icon-1x1.svg └── international-icon-3x2.svg ├── .npmignore ├── LICENSE ├── rollup.config.mjs ├── CODE_OF_CONDUCT.md └── style.css /flags/index.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require('country-flag-icons/react/3x2') -------------------------------------------------------------------------------- /flags/index.js: -------------------------------------------------------------------------------- 1 | export { default as default } from 'country-flag-icons/react/3x2' -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | import { should, expect } from 'chai' 2 | 3 | global.expect = expect 4 | should() -------------------------------------------------------------------------------- /react-hook-form-input-core/index.js: -------------------------------------------------------------------------------- 1 | export { default } from '../modules/react-hook-form/PhoneInput.js' -------------------------------------------------------------------------------- /react-hook-form-core/index.js: -------------------------------------------------------------------------------- 1 | export { default } from '../modules/react-hook-form/PhoneInputWithCountry.js' -------------------------------------------------------------------------------- /docs/images/first-glance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catamphetamine/react-phone-number-input/HEAD/docs/images/first-glance.png -------------------------------------------------------------------------------- /flags/index.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Flags 3 | } from '../index.d.js'; 4 | 5 | declare const flags: Flags; 6 | 7 | export default flags; -------------------------------------------------------------------------------- /docs/images/desktop-countries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catamphetamine/react-phone-number-input/HEAD/docs/images/desktop-countries.png -------------------------------------------------------------------------------- /docs/images/first-glance-local.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catamphetamine/react-phone-number-input/HEAD/docs/images/first-glance-local.png -------------------------------------------------------------------------------- /docs/images/iphone-countries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catamphetamine/react-phone-number-input/HEAD/docs/images/iphone-countries.png -------------------------------------------------------------------------------- /docs/images/iphone-native-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catamphetamine/react-phone-number-input/HEAD/docs/images/iphone-native-select.png -------------------------------------------------------------------------------- /docs/images/desktop-native-select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catamphetamine/react-phone-number-input/HEAD/docs/images/desktop-native-select.png -------------------------------------------------------------------------------- /locale/ar.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/ca.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/cz.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/de.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/el.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/en.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/es.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/et.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/fi.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/fr.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/he.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/hy.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/it.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/ja.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/ko.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/nb.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/nl.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/pl.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/pt.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/ru.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/sk.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/sv.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/th.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/tr.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/ua.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/vi.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/zh.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /locale/pt-BR.json.d.ts: -------------------------------------------------------------------------------- 1 | import { LabelKey } from '../index' 2 | type Locale = { [key in LabelKey]: string } 3 | declare const Locale: Locale 4 | export default Locale -------------------------------------------------------------------------------- /react-styleguidist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/react-styleguidist", 4 | "main": "styleguide.config.js" 5 | } 6 | -------------------------------------------------------------------------------- /source/libphonenumber/README.md: -------------------------------------------------------------------------------- 1 | These are proxies for [`libphonenumber-js`](https://gitlab.com/catamphetamine/libphonenumber-js) functions (with some API modifications for easier use). -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | sudo: false 5 | script: 6 | - "npm run test-coverage" 7 | after_success: 8 | - "npm install coveralls && npm run coveralls" -------------------------------------------------------------------------------- /react-native-input/index.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/min/metadata' 2 | 3 | import { createPhoneInput } from '../modules/react-native/PhoneInput.js' 4 | 5 | export default createPhoneInput(metadata) -------------------------------------------------------------------------------- /react-styleguidist/README.md: -------------------------------------------------------------------------------- 1 | This folder is a workaround for `Uncaught ReferenceError: module is not defined` bug of `react-styleguidist`. 2 | 3 | https://github.com/styleguidist/react-styleguidist/issues/2031 -------------------------------------------------------------------------------- /react-hook-form-input-core/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var PhoneInput = require('../commonjs/react-hook-form/PhoneInput.js').default 4 | 5 | exports = module.exports = PhoneInput 6 | 7 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /react-hook-form-input/index.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/min/metadata' 2 | 3 | import { createPhoneInput } from '../modules/react-hook-form/PhoneInput.js' 4 | 5 | export default createPhoneInput(metadata) -------------------------------------------------------------------------------- /react-hook-form/index.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/min/metadata' 2 | 3 | import { createPhoneInput } from '../modules/react-hook-form/PhoneInputWithCountry.js' 4 | 5 | export default createPhoneInput(metadata) -------------------------------------------------------------------------------- /react-hook-form-core/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var PhoneInputWithCountry = require('../commonjs/react-hook-form/PhoneInputWithCountry.js').default 4 | 5 | exports = module.exports = PhoneInputWithCountry 6 | 7 | exports['default'] = PhoneInputWithCountry -------------------------------------------------------------------------------- /source/PropTypes.js: -------------------------------------------------------------------------------- 1 | import PropTypes from 'prop-types' 2 | 3 | export const metadata = PropTypes.shape({ 4 | country_calling_codes : PropTypes.object.isRequired, 5 | countries : PropTypes.object.isRequired 6 | }) 7 | 8 | export const labels = PropTypes.objectOf(PropTypes.string) -------------------------------------------------------------------------------- /max/index.d.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default, 3 | parsePhoneNumber, 4 | formatPhoneNumber, 5 | formatPhoneNumberIntl, 6 | isValidPhoneNumber, 7 | isPossiblePhoneNumber, 8 | getCountryCallingCode, 9 | getCountries, 10 | isSupportedCountry, 11 | Country, 12 | Value, 13 | PhoneNumber 14 | } from '../index.d.js'; -------------------------------------------------------------------------------- /min/index.d.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default, 3 | parsePhoneNumber, 4 | formatPhoneNumber, 5 | formatPhoneNumberIntl, 6 | isValidPhoneNumber, 7 | isPossiblePhoneNumber, 8 | getCountryCallingCode, 9 | getCountries, 10 | isSupportedCountry, 11 | Country, 12 | Value, 13 | PhoneNumber 14 | } from '../index.d.js'; -------------------------------------------------------------------------------- /mobile/index.d.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default, 3 | parsePhoneNumber, 4 | formatPhoneNumber, 5 | formatPhoneNumberIntl, 6 | isValidPhoneNumber, 7 | isPossiblePhoneNumber, 8 | getCountryCallingCode, 9 | getCountries, 10 | isSupportedCountry, 11 | Country, 12 | Value, 13 | PhoneNumber 14 | } from '../index.d.js'; -------------------------------------------------------------------------------- /input-max/index.d.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default, 3 | parsePhoneNumber, 4 | formatPhoneNumber, 5 | formatPhoneNumberIntl, 6 | isValidPhoneNumber, 7 | isPossiblePhoneNumber, 8 | getCountryCallingCode, 9 | getCountries, 10 | isSupportedCountry, 11 | Country, 12 | Value, 13 | PhoneNumber 14 | } from '../input/index.d.js'; 15 | -------------------------------------------------------------------------------- /input-mobile/index.d.ts: -------------------------------------------------------------------------------- 1 | export { 2 | default, 3 | parsePhoneNumber, 4 | formatPhoneNumber, 5 | formatPhoneNumberIntl, 6 | isValidPhoneNumber, 7 | isPossiblePhoneNumber, 8 | getCountryCallingCode, 9 | getCountries, 10 | isSupportedCountry, 11 | Country, 12 | Value, 13 | PhoneNumber 14 | } from '../input/index.d.js'; 15 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "require": [ 3 | "@babel/register" 4 | ], 5 | "reporter": [ 6 | "lcov", 7 | "text-summary" 8 | ], 9 | "include": [ 10 | "source/helpers" 11 | ], 12 | "exclude": [ 13 | "**/*.test.js" 14 | ], 15 | "sourceMap": false, 16 | "instrument": false, 17 | "cache": true, 18 | "all": true 19 | } -------------------------------------------------------------------------------- /react-native-input/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var metadata = require('libphonenumber-js/min/metadata') 4 | var createPhoneInput = require('../commonjs/react-native/PhoneInput.js').createPhoneInput 5 | 6 | var PhoneInput = createPhoneInput(metadata) 7 | 8 | exports = module.exports = PhoneInput 9 | 10 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /react-hook-form-input/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var metadata = require('libphonenumber-js/min/metadata') 4 | var createPhoneInput = require('../commonjs/react-hook-form/PhoneInput.js').createPhoneInput 5 | 6 | var PhoneInput = createPhoneInput(metadata) 7 | 8 | exports = module.exports = PhoneInput 9 | 10 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/core", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /flags/index.cjs.js: -------------------------------------------------------------------------------- 1 | // This file is deprecated. 2 | // It's the same as `index.cjs`, just with an added `.js` file extension. 3 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 4 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 5 | 6 | module.exports = require('country-flag-icons/react/3x2') -------------------------------------------------------------------------------- /max/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/max", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /min/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/min", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /flags/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/flags", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /mobile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/mobile", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /test/exports.flags.test.js: -------------------------------------------------------------------------------- 1 | import Flags from '../flags/index.js' 2 | import Library from '../flags/index.cjs' 3 | 4 | describe('exports/flags', () => { 5 | it('should export ES6', () => { 6 | Flags.RU.should.be.a('function') 7 | }).timeout(60000) 8 | 9 | it('should export CommonJS', () => { 10 | Library.RU.should.be.a('function') 11 | }).timeout(60000) 12 | }) -------------------------------------------------------------------------------- /input-max/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/input-max", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /input/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/input-min", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /input-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/input-core", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /input-mobile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/input-mobile", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /react-hook-form/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var metadata = require('libphonenumber-js/min/metadata') 4 | var createPhoneInput = require('../commonjs/react-hook-form/PhoneInputWithCountry.js').createPhoneInput 5 | 6 | var PhoneInputWithCountry = createPhoneInput(metadata) 7 | 8 | exports = module.exports = PhoneInputWithCountry 9 | 10 | exports['default'] = PhoneInputWithCountry -------------------------------------------------------------------------------- /react-hook-form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/react-hook-form", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /react-native-input/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/react-native-input", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /react-hook-form-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/react-hook-form-core", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /react-hook-form-input/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/react-hook-form-input", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /react-hook-form-input-core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "react-phone-number-input/react-hook-form-input-core", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "types": "./index.d.ts", 10 | "import": "./index.js", 11 | "require": "./index.cjs" 12 | } 13 | }, 14 | "sideEffects": false 15 | } 16 | -------------------------------------------------------------------------------- /core/index.js: -------------------------------------------------------------------------------- 1 | export { default as default } from '../modules/PhoneInputWithCountry.js' 2 | export { default as formatPhoneNumber, formatPhoneNumberIntl } from '../modules/libphonenumber/formatPhoneNumber.js' 3 | 4 | export { 5 | default as parsePhoneNumber, 6 | isValidPhoneNumber, 7 | isPossiblePhoneNumber, 8 | getCountryCallingCode, 9 | getCountries, 10 | isSupportedCountry 11 | } from 'libphonenumber-js/core' -------------------------------------------------------------------------------- /input-core/index.js: -------------------------------------------------------------------------------- 1 | export { default as default } from '../modules/PhoneInputBrowser.js' 2 | 3 | export { 4 | default as parsePhoneNumber, 5 | isValidPhoneNumber, 6 | isPossiblePhoneNumber, 7 | getCountries, 8 | getCountryCallingCode, 9 | isSupportedCountry 10 | } from 'libphonenumber-js/core' 11 | 12 | export { default as formatPhoneNumber, formatPhoneNumberIntl } from '../modules/libphonenumber/formatPhoneNumber.js' 13 | -------------------------------------------------------------------------------- /scripts/create-commonjs-package-json.js: -------------------------------------------------------------------------------- 1 | // Creates a `package.json` file in the CommonJS build folder. 2 | // That marks that whole folder as CommonJS so that Node.js doesn't complain 3 | // about `require()`-ing those files. 4 | 5 | import fs from 'fs' 6 | 7 | fs.writeFileSync('./commonjs/package.json', JSON.stringify({ 8 | name: 'react-phone-number-input/commonjs', 9 | type: 'commonjs', 10 | private: true 11 | }, null, 2), 'utf8') -------------------------------------------------------------------------------- /website/docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Phone Number Input Style Guide 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: node:18 2 | 3 | pages: 4 | script: 5 | - npm install 6 | - mkdir ./react-styleguidist/project 7 | - cp .babelrc ./react-styleguidist/project/ 8 | - cp --recursive ./source ./react-styleguidist/project/ 9 | - npm run generate-docs:core 10 | - npm run build 11 | - mv ./bundle ./public 12 | - cp --recursive ./website/* ./public/ 13 | 14 | artifacts: 15 | paths: 16 | - public 17 | 18 | only: 19 | - master 20 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | 7 | "plugins": [ 8 | ["@babel/plugin-transform-for-of", { loose: true }] 9 | ], 10 | 11 | "env": { 12 | "es6": { 13 | "presets": [ 14 | ["@babel/preset-env", { modules: false }] 15 | ] 16 | }, 17 | "test": { 18 | "plugins": ["babel-plugin-istanbul"], 19 | "ignore": [ 20 | "bundle", 21 | "modules", 22 | "commonjs" 23 | ] 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /react-hook-form-input-core/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var PhoneInput = require('../commonjs/react-hook-form/PhoneInput.js').default 9 | 10 | exports = module.exports = PhoneInput 11 | 12 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /react-hook-form-core/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var PhoneInputWithCountry = require('../commonjs/react-hook-form/PhoneInputWithCountry.js').default 9 | 10 | exports = module.exports = PhoneInputWithCountry 11 | 12 | exports['default'] = PhoneInputWithCountry -------------------------------------------------------------------------------- /test/exports.react-native-input.test.js: -------------------------------------------------------------------------------- 1 | // Commented out, because it would require installing `react-native` dependency. 2 | 3 | // import PhoneInput from '../react-native-input/index' 4 | 5 | // describe('exports/react-native-input', () => { 6 | // it('should export ES6', () => { 7 | // PhoneInput.render.should.be.a('function') 8 | // }) 9 | 10 | // it('should export CommonJS', () => { 11 | // const Library = require('../input/index.cjs') 12 | // Library.render.should.be.a('function') 13 | // Library.default.render.should.be.a('function') 14 | // }) 15 | // }) -------------------------------------------------------------------------------- /project.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "follow_symlinks": true, 6 | "path": ".", 7 | "folder_exclude_patterns": ["node_modules", ".nyc_output", "react-styleguidist", "coverage", "commonjs", "react-phone-number-input/lib", "project.sublime-workspace", "bundle", "npm-debug.log", "modules", "website/lib", "website/docs/build"], 8 | "file_exclude_patterns": ["package-lock.json"] 9 | } 10 | ], 11 | "settings": 12 | { 13 | "tab_size": 3, 14 | "translate_tabs_to_spaces": false, 15 | "trim_trailing_white_space_on_save": true 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/helpers/inputValuePrefix.js: -------------------------------------------------------------------------------- 1 | import { getCountryCallingCode } from 'libphonenumber-js/core' 2 | 3 | export function getPrefixForFormattingValueAsPhoneNumber({ 4 | inputFormat, 5 | country, 6 | metadata 7 | }) { 8 | return inputFormat === 'NATIONAL_PART_OF_INTERNATIONAL' ? 9 | `+${getCountryCallingCode(country, metadata)}` : 10 | '' 11 | } 12 | 13 | export function removePrefixFromFormattedPhoneNumber(value, prefix) { 14 | if (prefix) { 15 | value = value.slice(prefix.length) 16 | if (value[0] === ' ') { 17 | value = value.slice(1) 18 | } 19 | } 20 | return value 21 | } -------------------------------------------------------------------------------- /react-native-input/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var metadata = require('libphonenumber-js/min/metadata') 9 | var createPhoneInput = require('../commonjs/react-native/PhoneInput.js').createPhoneInput 10 | 11 | var PhoneInput = createPhoneInput(metadata) 12 | 13 | exports = module.exports = PhoneInput 14 | 15 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /react-hook-form-input/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var metadata = require('libphonenumber-js/min/metadata') 9 | var createPhoneInput = require('../commonjs/react-hook-form/PhoneInput.js').createPhoneInput 10 | 11 | var PhoneInput = createPhoneInput(metadata) 12 | 13 | exports = module.exports = PhoneInput 14 | 15 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /react-hook-form/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var metadata = require('libphonenumber-js/min/metadata') 9 | var createPhoneInput = require('../commonjs/react-hook-form/PhoneInputWithCountry.js').createPhoneInput 10 | 11 | var PhoneInputWithCountry = createPhoneInput(metadata) 12 | 13 | exports = module.exports = PhoneInputWithCountry 14 | 15 | exports['default'] = PhoneInputWithCountry -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # testing package 2 | /react-phone-number-input-*.tgz 3 | 4 | # test coverage folder 5 | /coverage/ 6 | /.nyc_output/ 7 | 8 | # npm modules 9 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 10 | /node_modules/ 11 | 12 | # npm errors 13 | npm-debug.log 14 | 15 | # github pages 16 | /gh-pages/ 17 | 18 | # for OS X users 19 | .DS_Store 20 | 21 | # cache files for sublime text 22 | *.tmlanguage.cache 23 | *.tmPreferences.cache 24 | *.stTheme.cache 25 | 26 | # workspace files are user-specific 27 | *.sublime-workspace 28 | /.idea 29 | 30 | # webpack build target folder 31 | /commonjs/ 32 | /modules/ 33 | 34 | /bundle/ 35 | 36 | /react-styleguidist/project -------------------------------------------------------------------------------- /scripts/fix-locale-import-in-default-component.js: -------------------------------------------------------------------------------- 1 | // Replaces `import en from '../locale/en.json.js'` 2 | // with `import en from '../locale/en.json'` 3 | // in a CommonJS version of the built modules 4 | // Because CommonJS can import JSON files directly 5 | // and stupid Node.js "ES Module" system can't. 6 | 7 | import fs from 'fs' 8 | 9 | fs.writeFileSync( 10 | './commonjs/PhoneInputWithCountryDefault.js', 11 | fs.readFileSync('./commonjs/PhoneInputWithCountryDefault.js', 'utf8').replace('/locale/en.json.js', '/locale/en.json'), 12 | 'utf8' 13 | ) 14 | 15 | fs.writeFileSync( 16 | './commonjs/PhoneInputWithCountryDefault.js.map', 17 | fs.readFileSync('./commonjs/PhoneInputWithCountryDefault.js.map', 'utf8').replace('/locale/en.json.js', '/locale/en.json'), 18 | 'utf8' 19 | ) -------------------------------------------------------------------------------- /react-styleguidist.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | components: "source/PhoneInputWithCountry.js", 3 | styleguideDir: "website/docs", 4 | usageMode: "expand", 5 | sortProps: props => props, 6 | dangerouslyUpdateWebpackConfig(webpackConfig, env) { 7 | webpackConfig.output.filename = 'build/bundle.js' 8 | webpackConfig.output.chunkFilename = 'build/[name].js' 9 | return webpackConfig 10 | }, 11 | webpackConfig: { 12 | module: { 13 | rules: [ 14 | // Babel loader will use your project’s babel.config.js 15 | { 16 | test: /\.jsx?$/, 17 | exclude: /node_modules/, 18 | loader: 'babel-loader' 19 | }, 20 | // Other loaders that are needed for your components 21 | { 22 | test: /\.css$/, 23 | use: ['style-loader', 'css-loader'] 24 | } 25 | ] 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /source/react-hook-form/PhoneInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import ReactHookFormInput from './ReactHookFormInput.js' 4 | import PhoneInput_ from '../PhoneInputBrowser.js' 5 | 6 | import { metadata as metadataType } from '../PropTypes.js' 7 | 8 | export function createPhoneInput(defaultMetadata) { 9 | let PhoneInput = ({ 10 | metadata = defaultMetadata, 11 | ...rest 12 | }, ref) => { 13 | return ( 14 | 20 | ) 21 | } 22 | 23 | PhoneInput = React.forwardRef(PhoneInput) 24 | 25 | PhoneInput.propTypes = { 26 | metadata: metadataType 27 | } 28 | 29 | return PhoneInput 30 | } 31 | 32 | export default createPhoneInput() -------------------------------------------------------------------------------- /source/PhoneInputWithCountryDefault.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import defaultLabels from '../locale/en.json.js' 5 | 6 | import { 7 | metadata as metadataPropType, 8 | labels as labelsPropType 9 | } from './PropTypes.js' 10 | 11 | import PhoneInput from './PhoneInputWithCountry.js' 12 | 13 | export function createPhoneInput(defaultMetadata) { 14 | const PhoneInputDefault = React.forwardRef(({ 15 | metadata = defaultMetadata, 16 | labels = defaultLabels, 17 | ...rest 18 | }, ref) => ( 19 | 25 | )) 26 | 27 | PhoneInputDefault.propTypes = { 28 | metadata: metadataPropType, 29 | labels: labelsPropType 30 | } 31 | 32 | return PhoneInputDefault 33 | } 34 | 35 | export default createPhoneInput() -------------------------------------------------------------------------------- /source/helpers/getInternationalPhoneNumberPrefix.test.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/min/metadata' 2 | 3 | import getInternationalPhoneNumberPrefix from './getInternationalPhoneNumberPrefix.js' 4 | 5 | describe('getInternationalPhoneNumberPrefix', () => { 6 | it('should prepend leading digits when generating international phone number prefix', () => { 7 | // No leading digits. 8 | getInternationalPhoneNumberPrefix('RU', metadata).should.equal('+7') 9 | 10 | // The "pre-fill with leading digits on country selection" feature had to be reverted. 11 | // https://gitlab.com/catamphetamine/react-phone-number-input/-/issues/10#note_1231042367 12 | // // Has "fixed" leading digits. 13 | // // https://gitlab.com/catamphetamine/react-phone-number-input/-/issues/10 14 | // getInternationalPhoneNumberPrefix('AS', metadata).should.equal('+1684') 15 | }) 16 | }) -------------------------------------------------------------------------------- /source/helpers/isE164Number.js: -------------------------------------------------------------------------------- 1 | // Tells if `value: string` is an `E.164` phone number. 2 | // 3 | // Returns a boolean. 4 | // 5 | // It doesn't validate that the minimum national (significant) number length 6 | // is at least 2 characters. 7 | // 8 | export default function isE164Number(value) { 9 | if (value.length < 2) { 10 | return false 11 | } 12 | if (value[0] !== '+') { 13 | return false 14 | } 15 | let i = 1 16 | while (i < value.length) { 17 | const character = value.charCodeAt(i) 18 | if (character >= 48 && character <= 57) { 19 | // Is a digit. 20 | } else { 21 | return false 22 | } 23 | i++ 24 | } 25 | return true 26 | } 27 | 28 | export function validateE164Number(value) { 29 | if (!isE164Number(value)) { 30 | console.error('[react-phone-number-input] Expected the initial `value` to be a E.164 phone number. Got', value) 31 | } 32 | } -------------------------------------------------------------------------------- /input-core/index.cjs: -------------------------------------------------------------------------------- 1 | var Input = require('../commonjs/PhoneInputBrowser.js').default 2 | 3 | exports = module.exports = Input 4 | exports['default'] = Input 5 | 6 | exports.formatPhoneNumber = require('../commonjs/libphonenumber/formatPhoneNumber.js').default 7 | exports.formatPhoneNumberIntl = require('../commonjs/libphonenumber/formatPhoneNumber.js').formatPhoneNumberIntl 8 | 9 | exports.parsePhoneNumber = require('libphonenumber-js/core').default 10 | exports.isValidPhoneNumber = require('libphonenumber-js/core').isValidPhoneNumber 11 | exports.isPossiblePhoneNumber = require('libphonenumber-js/core').isPossiblePhoneNumber 12 | exports.getCountries = require('libphonenumber-js/core').getCountries 13 | exports.getCountryCallingCode = require('libphonenumber-js/core').getCountryCallingCode 14 | exports.isSupportedCountry = require('libphonenumber-js/core').isSupportedCountry 15 | -------------------------------------------------------------------------------- /react-styleguidist/styleguide.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | components: "project/source/PhoneInputWithCountry.js", 3 | styleguideDir: "../website/docs", 4 | usageMode: "expand", 5 | sortProps: props => props, 6 | dangerouslyUpdateWebpackConfig(webpackConfig, env) { 7 | webpackConfig.output.filename = 'build/bundle.js' 8 | webpackConfig.output.chunkFilename = 'build/[name].js' 9 | return webpackConfig 10 | }, 11 | webpackConfig: { 12 | module: { 13 | rules: [ 14 | // Babel loader will use your project’s babel.config.js 15 | { 16 | test: /\.jsx?$/, 17 | exclude: /node_modules/, 18 | loader: 'babel-loader', 19 | options: { 20 | babelrcRoots: ['..'] 21 | } 22 | }, 23 | // Other loaders that are needed for your components 24 | { 25 | test: /\.css$/, 26 | use: ['style-loader', 'css-loader'] 27 | } 28 | ] 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /core/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | exports = module.exports = require('../commonjs/PhoneInputWithCountry.js').default 4 | 5 | exports.formatPhoneNumber = require('../commonjs/libphonenumber/formatPhoneNumber.js').default 6 | exports.formatPhoneNumberIntl = require('../commonjs/libphonenumber/formatPhoneNumber.js').formatPhoneNumberIntl 7 | 8 | exports.parsePhoneNumber = require('libphonenumber-js/core').default 9 | exports.isValidPhoneNumber = require('libphonenumber-js/core').isValidPhoneNumber 10 | exports.isPossiblePhoneNumber = require('libphonenumber-js/core').isPossiblePhoneNumber 11 | exports.getCountries = require('libphonenumber-js/core').getCountries 12 | exports.getCountryCallingCode = require('libphonenumber-js/core').getCountryCallingCode 13 | exports.isSupportedCountry = require('libphonenumber-js/core').isSupportedCountry 14 | 15 | exports['default'] = require('../commonjs/PhoneInputWithCountry.js').default -------------------------------------------------------------------------------- /icons/international-icon-1x1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /source/react-hook-form/PhoneInputWithCountry.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import ReactHookFormInput from './ReactHookFormInput.js' 5 | import PhoneInputWithCountry_ from '../PhoneInputWithCountryDefault.js' 6 | 7 | import { metadata as metadataType } from '../PropTypes.js' 8 | 9 | export function createPhoneInput(defaultMetadata) { 10 | let PhoneInputWithCountry = ({ 11 | metadata = defaultMetadata, 12 | ...rest 13 | }, ref) => { 14 | return ( 15 | 21 | ) 22 | } 23 | 24 | PhoneInputWithCountry = React.forwardRef(PhoneInputWithCountry) 25 | 26 | PhoneInputWithCountry.propTypes = { 27 | metadata: metadataType 28 | } 29 | 30 | return PhoneInputWithCountry 31 | } 32 | 33 | export default createPhoneInput() -------------------------------------------------------------------------------- /source/helpers/isE164Number.test.js: -------------------------------------------------------------------------------- 1 | import isE164Number, { validateE164Number } from './isE164Number.js' 2 | 3 | describe('isE164Number', () => { 4 | it('should tell if a value is an E.164 phone number', () => { 5 | isE164Number('').should.equal(false) 6 | isE164Number('a').should.equal(false) 7 | isE164Number('aa').should.equal(false) 8 | isE164Number('1').should.equal(false) 9 | isE164Number('11').should.equal(false) 10 | isE164Number('111').should.equal(false) 11 | isE164Number('+').should.equal(false) 12 | isE164Number('+1').should.equal(true) 13 | isE164Number('+11').should.equal(true) 14 | isE164Number('+111').should.equal(true) 15 | isE164Number('+1a').should.equal(false) 16 | isE164Number('+aa').should.equal(false) 17 | isE164Number('+a1').should.equal(false) 18 | }) 19 | 20 | it('should validate that a value is an E.164 phone number', () => { 21 | validateE164Number('a') 22 | validateE164Number('+78005553535') 23 | }) 24 | }) -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # git 2 | .gitignore 3 | .gitattributes 4 | 5 | # Babel 6 | .babelrc 7 | 8 | # Sources aren't needed for npm 9 | /source 10 | 11 | # testing package 12 | /react-phone-number-input-*.tgz 13 | 14 | # Travis CI 15 | .travis.yml 16 | 17 | # test coverage folder 18 | /coverage/ 19 | /.nyc_output/ 20 | 21 | # npm errors 22 | npm-debug.log 23 | 24 | # github pages 25 | /gh-pages/ 26 | 27 | # for OS X users 28 | .DS_Store 29 | 30 | # cache files for sublime text 31 | *.tmlanguage.cache 32 | *.tmPreferences.cache 33 | *.stTheme.cache 34 | 35 | # workspace files are user-specific 36 | *.sublime-workspace 37 | *.sublime-project 38 | 39 | # webpack is used in development 40 | /webpack.config.babel.js 41 | 42 | # tests aren't needed for npm 43 | /test/ 44 | 45 | # bundle for the browser 46 | /bundle/flags 47 | /bundle/docs 48 | /bundle/lib 49 | /bundle/index.html 50 | /bundle/libphonenumber-js.min.js 51 | /bundle/libphonenumber-js.min.js.map 52 | 53 | /website/ -------------------------------------------------------------------------------- /scripts/verify-flag-existence.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import { hasFlag } from 'country-flag-icons' 3 | import { getCountries } from 'libphonenumber-js' 4 | 5 | // Validate `country-flag-icons`. 6 | for (const country of getCountries()) { 7 | if (!hasFlag(country)) { 8 | throw new Error(`${country} flag not found in "country-flag-icons"`) 9 | } 10 | } 11 | 12 | // // Validate `flagpack`. 13 | // for (const country of getCountries()) { 14 | // if (!fs.existsSync(`./node_modules/flagpack/flags/4x3/${country.toLowerCase()}.svg`)) { 15 | // // Currently, they don't have a couple of flags. 16 | // // I've submitted a pull request to `flagpack` repo: 17 | // // https://github.com/jackiboy/flagpack/pull/4 18 | // // The issue: 19 | // // https://github.com/jackiboy/flagpack/issues/3 20 | // if (country === 'AC' || country === 'TA') { 21 | // continue 22 | // } 23 | // throw new Error(`${country} flag not found in "flagpack"`) 24 | // } 25 | // } 26 | -------------------------------------------------------------------------------- /icons/international-icon-3x2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /scripts/build-bundle-styles.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | 4 | import autoprefixer from 'autoprefixer' 5 | import postcssCustomProperties from 'postcss-custom-properties' 6 | import postcss from 'postcss' 7 | 8 | RegExp.escape = function (string) { 9 | const specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g") 10 | return string.replace(specials, "\\$&") 11 | } 12 | 13 | String.prototype.replace_all = function (what, with_what) { 14 | const regexp = new RegExp(RegExp.escape(what), "g") 15 | return this.replace(regexp, with_what) 16 | } 17 | 18 | function transformStyle(inPath, outPath) { 19 | outPath = outPath || inPath 20 | const text = fs.readFileSync(path.resolve(inPath), 'utf8') 21 | return postcss([ 22 | autoprefixer(), 23 | postcssCustomProperties() 24 | ]).process(text, { from: undefined }).then((result) => { 25 | result.warnings().forEach((warn) => console.warn(warn.toString())) 26 | fs.writeFileSync(path.resolve(outPath), result.css) 27 | }) 28 | } 29 | 30 | Promise.all([ 31 | transformStyle('./style.css', './bundle/style.css') 32 | ]) 33 | -------------------------------------------------------------------------------- /input-core/index.d.ts: -------------------------------------------------------------------------------- 1 | // React TypeScript Cheatsheet doesn't recommend using `React.FunctionalComponent`. 2 | // https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components 3 | 4 | import * as React from 'react'; 5 | 6 | import { 7 | Metadata, 8 | DefaultInputComponentProps 9 | } from '../index.d.js'; 10 | 11 | import { 12 | Props as BaseProps 13 | } from '../input/index.d.js'; 14 | 15 | type Props = BaseProps & { 16 | metadata: Metadata; 17 | } 18 | 19 | type PhoneInputComponentType = React.ForwardRefExoticComponent & React.RefAttributes> 20 | 21 | declare const PhoneInput: PhoneInputComponentType; 22 | 23 | export default PhoneInput; 24 | 25 | export { 26 | parsePhoneNumber, 27 | formatPhoneNumber, 28 | formatPhoneNumberIntl, 29 | isValidPhoneNumber, 30 | isPossiblePhoneNumber, 31 | getCountryCallingCode, 32 | getCountries, 33 | isSupportedCountry, 34 | Country, 35 | Value, 36 | PhoneNumber 37 | } from '../core/index.d.js'; 38 | -------------------------------------------------------------------------------- /source/libphonenumber/formatPhoneNumber.js: -------------------------------------------------------------------------------- 1 | import parsePhoneNumber from 'libphonenumber-js/core' 2 | 3 | /** 4 | * Formats a phone number. 5 | * Is a proxy for `libphonenumber-js`'s `.format()` function of a parsed `PhoneNumber`. 6 | * @param {string} value 7 | * @param {string} [format] 8 | * @param {object} metadata 9 | * @return {string} 10 | */ 11 | export default function formatPhoneNumber(value, format, metadata) { 12 | if (!metadata) { 13 | if (typeof format === 'object') { 14 | metadata = format 15 | format = 'NATIONAL' 16 | } 17 | } 18 | if (!value) { 19 | return '' 20 | } 21 | const phoneNumber = parsePhoneNumber(value, metadata) 22 | if (!phoneNumber) { 23 | return '' 24 | } 25 | // Deprecated. 26 | // Legacy `format`s. 27 | switch (format) { 28 | case 'National': 29 | format = 'NATIONAL' 30 | break 31 | case 'International': 32 | format = 'INTERNATIONAL' 33 | break 34 | } 35 | return phoneNumber.format(format) 36 | } 37 | 38 | export function formatPhoneNumberIntl(value, metadata) { 39 | return formatPhoneNumber(value, 'INTERNATIONAL', metadata) 40 | } -------------------------------------------------------------------------------- /source/helpers/inputValuePrefix.test.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/min/metadata' 2 | 3 | import { getPrefixForFormattingValueAsPhoneNumber, removePrefixFromFormattedPhoneNumber } from './inputValuePrefix.js' 4 | 5 | describe('inputValuePrefix', () => { 6 | it('should get input value prefix', () => { 7 | getPrefixForFormattingValueAsPhoneNumber({ 8 | country: 'RU', 9 | metadata 10 | }).should.equal('') 11 | 12 | getPrefixForFormattingValueAsPhoneNumber({ 13 | country: 'RU', 14 | inputFormat: 'INTERNATIONAL', 15 | metadata 16 | }).should.equal('') 17 | 18 | getPrefixForFormattingValueAsPhoneNumber({ 19 | country: 'RU', 20 | inputFormat: 'NATIONAL_PART_OF_INTERNATIONAL', 21 | metadata 22 | }).should.equal('+7') 23 | }) 24 | 25 | it('should remove input value prefix', () => { 26 | removePrefixFromFormattedPhoneNumber('+78005553535', '+7').should.equal('8005553535') 27 | removePrefixFromFormattedPhoneNumber('+7 800 555 35 35', '+7').should.equal('800 555 35 35') 28 | removePrefixFromFormattedPhoneNumber('8 (800) 555-35-35', '').should.equal('8 (800) 555-35-35') 29 | }) 30 | }) -------------------------------------------------------------------------------- /core/index.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Metadata, 3 | Labels, 4 | Props as BaseProps, 5 | State, 6 | Value, 7 | ExternalValue, 8 | DefaultInputComponentProps 9 | } from '../index.d.js'; 10 | 11 | export { 12 | Country, 13 | Value 14 | } from '../index.d.js'; 15 | 16 | type Props = BaseProps & { 17 | metadata: Metadata; 18 | labels: Labels; 19 | } 20 | 21 | type PhoneInputWithCountrySelectType = React.ComponentClass, State>>; 22 | 23 | declare const PhoneInputWithCountrySelect: PhoneInputWithCountrySelectType; 24 | 25 | export default PhoneInputWithCountrySelect; 26 | 27 | export function formatPhoneNumber(value: Value | ExternalValue, metadata: Metadata): string; 28 | export function formatPhoneNumberIntl(value: Value | ExternalValue, metadata: Metadata): string; 29 | 30 | export { 31 | default as parsePhoneNumber, 32 | isValidPhoneNumber, 33 | isPossiblePhoneNumber, 34 | getCountryCallingCode, 35 | getCountries, 36 | isSupportedCountry, 37 | PhoneNumber 38 | } from 'libphonenumber-js/core'; 39 | -------------------------------------------------------------------------------- /input-core/index.cjs.js: -------------------------------------------------------------------------------- 1 | // This file is deprecated. 2 | // It's the same as `index.cjs`, just with an added `.js` file extension. 3 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 4 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 5 | 6 | var Input = require('../commonjs/PhoneInputBrowser.js').default 7 | 8 | exports = module.exports = Input 9 | exports['default'] = Input 10 | 11 | exports.formatPhoneNumber = require('../commonjs/libphonenumber/formatPhoneNumber.js').default 12 | exports.formatPhoneNumberIntl = require('../commonjs/libphonenumber/formatPhoneNumber.js').formatPhoneNumberIntl 13 | 14 | exports.parsePhoneNumber = require('libphonenumber-js/core').default 15 | exports.isValidPhoneNumber = require('libphonenumber-js/core').isValidPhoneNumber 16 | exports.isPossiblePhoneNumber = require('libphonenumber-js/core').isPossiblePhoneNumber 17 | exports.getCountries = require('libphonenumber-js/core').getCountries 18 | exports.getCountryCallingCode = require('libphonenumber-js/core').getCountryCallingCode 19 | exports.isSupportedCountry = require('libphonenumber-js/core').isSupportedCountry 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2016 @catamphetamine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /core/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | exports = module.exports = require('../commonjs/PhoneInputWithCountry.js').default 9 | 10 | exports.formatPhoneNumber = require('../commonjs/libphonenumber/formatPhoneNumber.js').default 11 | exports.formatPhoneNumberIntl = require('../commonjs/libphonenumber/formatPhoneNumber.js').formatPhoneNumberIntl 12 | 13 | exports.parsePhoneNumber = require('libphonenumber-js/core').default 14 | exports.isValidPhoneNumber = require('libphonenumber-js/core').isValidPhoneNumber 15 | exports.isPossiblePhoneNumber = require('libphonenumber-js/core').isPossiblePhoneNumber 16 | exports.getCountries = require('libphonenumber-js/core').getCountries 17 | exports.getCountryCallingCode = require('libphonenumber-js/core').getCountryCallingCode 18 | exports.isSupportedCountry = require('libphonenumber-js/core').isSupportedCountry 19 | 20 | exports['default'] = require('../commonjs/PhoneInputWithCountry.js').default -------------------------------------------------------------------------------- /source/react-native/PhoneInput.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import PhoneTextInput from './PhoneTextInput.js' 5 | import PhoneInput_ from '../PhoneInput.js' 6 | import InputBasic from '../InputBasic.js' 7 | 8 | import { metadata as metadataType } from '../PropTypes.js' 9 | 10 | /** 11 | * This is an _experimental_ React Native component. 12 | * Feedback thread: https://github.com/catamphetamine/react-phone-number-input/issues/296 13 | */ 14 | export function createPhoneInput(defaultMetadata) { 15 | let PhoneInput = ({ 16 | inputComponent, 17 | metadata = defaultMetadata, 18 | ...rest 19 | }, ref) => ( 20 | 28 | ) 29 | 30 | PhoneInput = React.forwardRef(PhoneInput) 31 | 32 | PhoneInput.propTypes = { 33 | /** 34 | * Allows specifying a custom input field component, 35 | * like a "Material UI" input field or something. 36 | */ 37 | inputComponent: PropTypes.elementType, 38 | 39 | /** 40 | * `libphonenumber-js` metadata. 41 | */ 42 | metadata: metadataType 43 | } 44 | 45 | return PhoneInput 46 | } 47 | 48 | export default createPhoneInput() -------------------------------------------------------------------------------- /source/helpers/parsePhoneNumberCharacter.test.js: -------------------------------------------------------------------------------- 1 | import parsePhoneNumberCharacter from './parsePhoneNumberCharacter.js' 2 | 3 | describe('parsePhoneNumberCharacter', () => { 4 | it('should work with a new `context` argument in `parsePhoneNumberCharacter()` function (international number)', () => { 5 | const context = {} 6 | 7 | parsePhoneNumberCharacter('+', undefined, context).should.equal('+') 8 | expect(context).to.deep.equal({}) 9 | 10 | parsePhoneNumberCharacter('1', '+', context).should.equal('1') 11 | expect(context).to.deep.equal({}) 12 | 13 | expect(parsePhoneNumberCharacter('+', '+1', context)).to.equal(undefined) 14 | expect(context).to.deep.equal({ ignoreRest: true }) 15 | 16 | expect(parsePhoneNumberCharacter('2', '+1', context)).to.equal(undefined) 17 | expect(context).to.deep.equal({ ignoreRest: true }) 18 | }) 19 | 20 | it('should work with a new `context` argument in `parsePhoneNumberCharacter()` function (national number)', () => { 21 | const context = {} 22 | 23 | parsePhoneNumberCharacter('2', undefined, context).should.equal('2') 24 | expect(context).to.deep.equal({}) 25 | 26 | expect(parsePhoneNumberCharacter('+', '2', context)).to.equal(undefined) 27 | expect(context).to.deep.equal({ ignoreRest: true }) 28 | 29 | expect(parsePhoneNumberCharacter('1', '2', context)).to.equal(undefined) 30 | expect(context).to.deep.equal({ ignoreRest: true }) 31 | }) 32 | }) -------------------------------------------------------------------------------- /input/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var metadata = require('libphonenumber-js/min/metadata') 4 | var core = require('../core/index.cjs') 5 | var createInput = require('../commonjs/PhoneInputBrowser.js').createInput 6 | 7 | function call(func, _arguments) { 8 | var args = Array.prototype.slice.call(_arguments) 9 | args.push(metadata) 10 | return func.apply(this, args) 11 | } 12 | 13 | var PhoneInput = createInput(metadata) 14 | 15 | exports = module.exports = PhoneInput 16 | 17 | exports.parsePhoneNumber = function parsePhoneNumber() { 18 | return call(core.parsePhoneNumber, arguments) 19 | } 20 | 21 | exports.formatPhoneNumber = function formatPhoneNumber() { 22 | return call(core.formatPhoneNumber, arguments) 23 | } 24 | 25 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 26 | return call(core.formatPhoneNumberIntl, arguments) 27 | } 28 | 29 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 30 | return call(core.isValidPhoneNumber, arguments) 31 | } 32 | 33 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 34 | return call(core.isPossiblePhoneNumber, arguments) 35 | } 36 | 37 | exports.getCountries = function getCountries() { 38 | return call(core.getCountries, arguments) 39 | } 40 | 41 | exports.getCountryCallingCode = function getCountryCallingCode() { 42 | return call(core.getCountryCallingCode, arguments) 43 | } 44 | 45 | exports.isSupportedCountry = function isSupportedCountry() { 46 | return call(core.isSupportedCountry, arguments) 47 | } 48 | 49 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /input-max/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var metadata = require('libphonenumber-js/max/metadata') 4 | var core = require('../core/index.cjs') 5 | var createInput = require('../commonjs/PhoneInputBrowser.js').createInput 6 | 7 | function call(func, _arguments) { 8 | var args = Array.prototype.slice.call(_arguments) 9 | args.push(metadata) 10 | return func.apply(this, args) 11 | } 12 | 13 | var PhoneInput = createInput(metadata) 14 | 15 | exports = module.exports = PhoneInput 16 | 17 | exports.parsePhoneNumber = function parsePhoneNumber() { 18 | return call(core.parsePhoneNumber, arguments) 19 | } 20 | 21 | exports.formatPhoneNumber = function formatPhoneNumber() { 22 | return call(core.formatPhoneNumber, arguments) 23 | } 24 | 25 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 26 | return call(core.formatPhoneNumberIntl, arguments) 27 | } 28 | 29 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 30 | return call(core.isValidPhoneNumber, arguments) 31 | } 32 | 33 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 34 | return call(core.isPossiblePhoneNumber, arguments) 35 | } 36 | 37 | exports.getCountries = function getCountries() { 38 | return call(core.getCountries, arguments) 39 | } 40 | 41 | exports.getCountryCallingCode = function getCountryCallingCode() { 42 | return call(core.getCountryCallingCode, arguments) 43 | } 44 | 45 | exports.isSupportedCountry = function isSupportedCountry() { 46 | return call(core.isSupportedCountry, arguments) 47 | } 48 | 49 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /input-mobile/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var metadata = require('libphonenumber-js/mobile/metadata') 4 | var core = require('../core/index.cjs') 5 | var createInput = require('../commonjs/PhoneInputBrowser.js').createInput 6 | 7 | function call(func, _arguments) { 8 | var args = Array.prototype.slice.call(_arguments) 9 | args.push(metadata) 10 | return func.apply(this, args) 11 | } 12 | 13 | var PhoneInput = createInput(metadata) 14 | 15 | exports = module.exports = PhoneInput 16 | 17 | exports.parsePhoneNumber = function parsePhoneNumber() { 18 | return call(core.parsePhoneNumber, arguments) 19 | } 20 | 21 | exports.formatPhoneNumber = function formatPhoneNumber() { 22 | return call(core.formatPhoneNumber, arguments) 23 | } 24 | 25 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 26 | return call(core.formatPhoneNumberIntl, arguments) 27 | } 28 | 29 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 30 | return call(core.isValidPhoneNumber, arguments) 31 | } 32 | 33 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 34 | return call(core.isPossiblePhoneNumber, arguments) 35 | } 36 | 37 | exports.getCountries = function getCountries() { 38 | return call(core.getCountries, arguments) 39 | } 40 | 41 | exports.getCountryCallingCode = function getCountryCallingCode() { 42 | return call(core.getCountryCallingCode, arguments) 43 | } 44 | 45 | exports.isSupportedCountry = function isSupportedCountry() { 46 | return call(core.isSupportedCountry, arguments) 47 | } 48 | 49 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /max/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var metadata = require('libphonenumber-js/max/metadata') 4 | var core = require('../core/index.cjs') 5 | var createPhoneInput = require('../commonjs/PhoneInputWithCountryDefault.js').createPhoneInput 6 | 7 | function call(func, _arguments) { 8 | var args = Array.prototype.slice.call(_arguments) 9 | args.push(metadata) 10 | return func.apply(this, args) 11 | } 12 | 13 | var PhoneInput = createPhoneInput(metadata) 14 | 15 | exports = module.exports = PhoneInput 16 | 17 | exports.parsePhoneNumber = function parsePhoneNumber() { 18 | return call(core.parsePhoneNumber, arguments) 19 | } 20 | 21 | exports.formatPhoneNumber = function formatPhoneNumber() { 22 | return call(core.formatPhoneNumber, arguments) 23 | } 24 | 25 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 26 | return call(core.formatPhoneNumberIntl, arguments) 27 | } 28 | 29 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 30 | return call(core.isValidPhoneNumber, arguments) 31 | } 32 | 33 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 34 | return call(core.isPossiblePhoneNumber, arguments) 35 | } 36 | 37 | exports.getCountries = function getCountries() { 38 | return call(core.getCountries, arguments) 39 | } 40 | 41 | exports.getCountryCallingCode = function getCountryCallingCode() { 42 | return call(core.getCountryCallingCode, arguments) 43 | } 44 | 45 | exports.isSupportedCountry = function isSupportedCountry() { 46 | return call(core.isSupportedCountry, arguments) 47 | } 48 | 49 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /min/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var metadata = require('libphonenumber-js/min/metadata') 4 | var core = require('../core/index.cjs') 5 | var createPhoneInput = require('../commonjs/PhoneInputWithCountryDefault.js').createPhoneInput 6 | 7 | function call(func, _arguments) { 8 | var args = Array.prototype.slice.call(_arguments) 9 | args.push(metadata) 10 | return func.apply(this, args) 11 | } 12 | 13 | var PhoneInput = createPhoneInput(metadata) 14 | 15 | exports = module.exports = PhoneInput 16 | 17 | exports.parsePhoneNumber = function parsePhoneNumber() { 18 | return call(core.parsePhoneNumber, arguments) 19 | } 20 | 21 | exports.formatPhoneNumber = function formatPhoneNumber() { 22 | return call(core.formatPhoneNumber, arguments) 23 | } 24 | 25 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 26 | return call(core.formatPhoneNumberIntl, arguments) 27 | } 28 | 29 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 30 | return call(core.isValidPhoneNumber, arguments) 31 | } 32 | 33 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 34 | return call(core.isPossiblePhoneNumber, arguments) 35 | } 36 | 37 | exports.getCountries = function getCountries() { 38 | return call(core.getCountries, arguments) 39 | } 40 | 41 | exports.getCountryCallingCode = function getCountryCallingCode() { 42 | return call(core.getCountryCallingCode, arguments) 43 | } 44 | 45 | exports.isSupportedCountry = function isSupportedCountry() { 46 | return call(core.isSupportedCountry, arguments) 47 | } 48 | 49 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /mobile/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var metadata = require('libphonenumber-js/mobile/metadata') 4 | var core = require('../core/index.cjs') 5 | var createPhoneInput = require('../commonjs/PhoneInputWithCountryDefault.js').createPhoneInput 6 | 7 | function call(func, _arguments) { 8 | var args = Array.prototype.slice.call(_arguments) 9 | args.push(metadata) 10 | return func.apply(this, args) 11 | } 12 | 13 | var PhoneInput = createPhoneInput(metadata) 14 | 15 | exports = module.exports = PhoneInput 16 | 17 | exports.parsePhoneNumber = function parsePhoneNumber() { 18 | return call(core.parsePhoneNumber, arguments) 19 | } 20 | 21 | exports.formatPhoneNumber = function formatPhoneNumber() { 22 | return call(core.formatPhoneNumber, arguments) 23 | } 24 | 25 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 26 | return call(core.formatPhoneNumberIntl, arguments) 27 | } 28 | 29 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 30 | return call(core.isValidPhoneNumber, arguments) 31 | } 32 | 33 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 34 | return call(core.isPossiblePhoneNumber, arguments) 35 | } 36 | 37 | exports.getCountries = function getCountries() { 38 | return call(core.getCountries, arguments) 39 | } 40 | 41 | exports.getCountryCallingCode = function getCountryCallingCode() { 42 | return call(core.getCountryCallingCode, arguments) 43 | } 44 | 45 | exports.isSupportedCountry = function isSupportedCountry() { 46 | return call(core.isSupportedCountry, arguments) 47 | } 48 | 49 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /input/index.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/min/metadata' 2 | 3 | import { 4 | parsePhoneNumber as _parsePhoneNumber, 5 | formatPhoneNumber as _formatPhoneNumber, 6 | formatPhoneNumberIntl as _formatPhoneNumberIntl, 7 | isValidPhoneNumber as _isValidPhoneNumber, 8 | isPossiblePhoneNumber as _isPossiblePhoneNumber, 9 | getCountries as _getCountries, 10 | getCountryCallingCode as _getCountryCallingCode, 11 | isSupportedCountry as _isSupportedCountry 12 | } from '../core/index.js' 13 | 14 | import { createInput } from '../modules/PhoneInputBrowser.js' 15 | 16 | function call(func, _arguments) { 17 | var args = Array.prototype.slice.call(_arguments) 18 | args.push(metadata) 19 | return func.apply(this, args) 20 | } 21 | 22 | export default createInput(metadata) 23 | 24 | export function parsePhoneNumber() { 25 | return call(_parsePhoneNumber, arguments) 26 | } 27 | 28 | export function formatPhoneNumber() { 29 | return call(_formatPhoneNumber, arguments) 30 | } 31 | 32 | export function formatPhoneNumberIntl() { 33 | return call(_formatPhoneNumberIntl, arguments) 34 | } 35 | 36 | export function isValidPhoneNumber() { 37 | return call(_isValidPhoneNumber, arguments) 38 | } 39 | 40 | export function isPossiblePhoneNumber() { 41 | return call(_isPossiblePhoneNumber, arguments) 42 | } 43 | 44 | export function getCountries() { 45 | return call(_getCountries, arguments) 46 | } 47 | 48 | export function getCountryCallingCode() { 49 | return call(_getCountryCallingCode, arguments) 50 | } 51 | 52 | export function isSupportedCountry() { 53 | return call(_isSupportedCountry, arguments) 54 | } -------------------------------------------------------------------------------- /input-max/index.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/max/metadata' 2 | 3 | import { 4 | parsePhoneNumber as _parsePhoneNumber, 5 | formatPhoneNumber as _formatPhoneNumber, 6 | formatPhoneNumberIntl as _formatPhoneNumberIntl, 7 | isValidPhoneNumber as _isValidPhoneNumber, 8 | isPossiblePhoneNumber as _isPossiblePhoneNumber, 9 | getCountries as _getCountries, 10 | getCountryCallingCode as _getCountryCallingCode, 11 | isSupportedCountry as _isSupportedCountry 12 | } from '../core/index.js' 13 | 14 | import { createInput } from '../modules/PhoneInputBrowser.js' 15 | 16 | function call(func, _arguments) { 17 | var args = Array.prototype.slice.call(_arguments) 18 | args.push(metadata) 19 | return func.apply(this, args) 20 | } 21 | 22 | export default createInput(metadata) 23 | 24 | export function parsePhoneNumber() { 25 | return call(_parsePhoneNumber, arguments) 26 | } 27 | 28 | export function formatPhoneNumber() { 29 | return call(_formatPhoneNumber, arguments) 30 | } 31 | 32 | export function formatPhoneNumberIntl() { 33 | return call(_formatPhoneNumberIntl, arguments) 34 | } 35 | 36 | export function isValidPhoneNumber() { 37 | return call(_isValidPhoneNumber, arguments) 38 | } 39 | 40 | export function isPossiblePhoneNumber() { 41 | return call(_isPossiblePhoneNumber, arguments) 42 | } 43 | 44 | export function getCountries() { 45 | return call(_getCountries, arguments) 46 | } 47 | 48 | export function getCountryCallingCode() { 49 | return call(_getCountryCallingCode, arguments) 50 | } 51 | 52 | export function isSupportedCountry() { 53 | return call(_isSupportedCountry, arguments) 54 | } -------------------------------------------------------------------------------- /input-mobile/index.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/mobile/metadata' 2 | 3 | import { 4 | parsePhoneNumber as _parsePhoneNumber, 5 | formatPhoneNumber as _formatPhoneNumber, 6 | formatPhoneNumberIntl as _formatPhoneNumberIntl, 7 | isValidPhoneNumber as _isValidPhoneNumber, 8 | isPossiblePhoneNumber as _isPossiblePhoneNumber, 9 | getCountries as _getCountries, 10 | getCountryCallingCode as _getCountryCallingCode, 11 | isSupportedCountry as _isSupportedCountry 12 | } from '../core/index.js' 13 | 14 | import { createInput } from '../modules/PhoneInputBrowser.js' 15 | 16 | function call(func, _arguments) { 17 | var args = Array.prototype.slice.call(_arguments) 18 | args.push(metadata) 19 | return func.apply(this, args) 20 | } 21 | 22 | export default createInput(metadata) 23 | 24 | export function parsePhoneNumber() { 25 | return call(_parsePhoneNumber, arguments) 26 | } 27 | 28 | export function formatPhoneNumber() { 29 | return call(_formatPhoneNumber, arguments) 30 | } 31 | 32 | export function formatPhoneNumberIntl() { 33 | return call(_formatPhoneNumberIntl, arguments) 34 | } 35 | 36 | export function isValidPhoneNumber() { 37 | return call(_isValidPhoneNumber, arguments) 38 | } 39 | 40 | export function isPossiblePhoneNumber() { 41 | return call(_isPossiblePhoneNumber, arguments) 42 | } 43 | 44 | export function getCountries() { 45 | return call(_getCountries, arguments) 46 | } 47 | 48 | export function getCountryCallingCode() { 49 | return call(_getCountryCallingCode, arguments) 50 | } 51 | 52 | export function isSupportedCountry() { 53 | return call(_isSupportedCountry, arguments) 54 | } -------------------------------------------------------------------------------- /react-hook-form-input-core/index.d.ts: -------------------------------------------------------------------------------- 1 | // React TypeScript Cheatsheet doesn't recommend using `React.FunctionalComponent` (`React.FC`). 2 | // https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components 3 | 4 | import * as React from 'react'; 5 | 6 | import { FieldValues } from 'react-hook-form'; 7 | 8 | import { 9 | Metadata, 10 | DefaultInputComponentProps 11 | } from '../index.d.js'; 12 | 13 | export { 14 | Country, 15 | Value 16 | } from '../index.d.js'; 17 | 18 | import { 19 | Props as BaseProps 20 | } from '../react-hook-form-input/index.d.js'; 21 | 22 | import { 23 | DefaultFormValues 24 | } from '../react-hook-form/index.d.js'; 25 | 26 | type Props = BaseProps & { 27 | metadata: Metadata; 28 | } 29 | 30 | type PhoneInputType = (props: Props) => JSX.Element; 31 | 32 | // Could also export the component that would accept custom "generics", 33 | // but seems like it would also introduce some inconvenience when using `typeof PhoneInput` 34 | // for defining the type of the `props`. 35 | // https://github.com/catamphetamine/react-phone-number-input/issues/414#issuecomment-1220679025 36 | // type PhoneInputType = (props: Props) => JSX.Element; 37 | 38 | declare const PhoneInput: PhoneInputType; 39 | 40 | export default PhoneInput; -------------------------------------------------------------------------------- /source/helpers/getInternationalPhoneNumberPrefix.js: -------------------------------------------------------------------------------- 1 | import { 2 | getCountryCallingCode, 3 | Metadata 4 | } from 'libphonenumber-js/core' 5 | 6 | const ONLY_DIGITS_REGEXP = /^\d+$/ 7 | 8 | export default function getInternationalPhoneNumberPrefix(country, metadata) { 9 | // Standard international phone number prefix: "+" and "country calling code". 10 | let prefix = '+' + getCountryCallingCode(country, metadata) 11 | 12 | // "Leading digits" can't be used to rule out any countries. 13 | // So the "pre-fill with leading digits on country selection" feature had to be reverted. 14 | // https://gitlab.com/catamphetamine/react-phone-number-input/-/issues/10#note_1231042367 15 | // // Get "leading digits" for a phone number of the country. 16 | // // If there're "leading digits" then they can be part of the prefix too. 17 | // // https://gitlab.com/catamphetamine/react-phone-number-input/-/issues/10 18 | // metadata = new Metadata(metadata) 19 | // metadata.selectNumberingPlan(country) 20 | // // "Leading digits" patterns are only defined for about 20% of all countries. 21 | // // By definition, matching "leading digits" is a sufficient but not a necessary 22 | // // condition for a phone number to belong to a country. 23 | // // The point of "leading digits" check is that it's the fastest one to get a match. 24 | // // https://gitlab.com/catamphetamine/libphonenumber-js/blob/master/METADATA.md#leading_digits 25 | // const leadingDigits = metadata.numberingPlan.leadingDigits() 26 | // if (leadingDigits && ONLY_DIGITS_REGEXP.test(leadingDigits)) { 27 | // prefix += leadingDigits 28 | // } 29 | 30 | return prefix 31 | } -------------------------------------------------------------------------------- /max/index.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/max/metadata' 2 | 3 | import { 4 | parsePhoneNumber as _parsePhoneNumber, 5 | formatPhoneNumber as _formatPhoneNumber, 6 | formatPhoneNumberIntl as _formatPhoneNumberIntl, 7 | isValidPhoneNumber as _isValidPhoneNumber, 8 | isPossiblePhoneNumber as _isPossiblePhoneNumber, 9 | getCountries as _getCountries, 10 | getCountryCallingCode as _getCountryCallingCode, 11 | isSupportedCountry as _isSupportedCountry 12 | } from '../core/index.js' 13 | 14 | import { createPhoneInput } from '../modules/PhoneInputWithCountryDefault.js' 15 | 16 | function call(func, _arguments) { 17 | var args = Array.prototype.slice.call(_arguments) 18 | args.push(metadata) 19 | return func.apply(this, args) 20 | } 21 | 22 | export default createPhoneInput(metadata) 23 | 24 | export function parsePhoneNumber() { 25 | return call(_parsePhoneNumber, arguments) 26 | } 27 | 28 | export function formatPhoneNumber() { 29 | return call(_formatPhoneNumber, arguments) 30 | } 31 | 32 | export function formatPhoneNumberIntl() { 33 | return call(_formatPhoneNumberIntl, arguments) 34 | } 35 | 36 | export function isValidPhoneNumber() { 37 | return call(_isValidPhoneNumber, arguments) 38 | } 39 | 40 | export function isPossiblePhoneNumber() { 41 | return call(_isPossiblePhoneNumber, arguments) 42 | } 43 | 44 | export function getCountries() { 45 | return call(_getCountries, arguments) 46 | } 47 | 48 | export function getCountryCallingCode() { 49 | return call(_getCountryCallingCode, arguments) 50 | } 51 | 52 | export function isSupportedCountry() { 53 | return call(_isSupportedCountry, arguments) 54 | } -------------------------------------------------------------------------------- /min/index.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/min/metadata' 2 | 3 | import { 4 | parsePhoneNumber as _parsePhoneNumber, 5 | formatPhoneNumber as _formatPhoneNumber, 6 | formatPhoneNumberIntl as _formatPhoneNumberIntl, 7 | isValidPhoneNumber as _isValidPhoneNumber, 8 | isPossiblePhoneNumber as _isPossiblePhoneNumber, 9 | getCountries as _getCountries, 10 | getCountryCallingCode as _getCountryCallingCode, 11 | isSupportedCountry as _isSupportedCountry 12 | } from '../core/index.js' 13 | 14 | import { createPhoneInput } from '../modules/PhoneInputWithCountryDefault.js' 15 | 16 | function call(func, _arguments) { 17 | var args = Array.prototype.slice.call(_arguments) 18 | args.push(metadata) 19 | return func.apply(this, args) 20 | } 21 | 22 | export default createPhoneInput(metadata) 23 | 24 | export function parsePhoneNumber() { 25 | return call(_parsePhoneNumber, arguments) 26 | } 27 | 28 | export function formatPhoneNumber() { 29 | return call(_formatPhoneNumber, arguments) 30 | } 31 | 32 | export function formatPhoneNumberIntl() { 33 | return call(_formatPhoneNumberIntl, arguments) 34 | } 35 | 36 | export function isValidPhoneNumber() { 37 | return call(_isValidPhoneNumber, arguments) 38 | } 39 | 40 | export function isPossiblePhoneNumber() { 41 | return call(_isPossiblePhoneNumber, arguments) 42 | } 43 | 44 | export function getCountries() { 45 | return call(_getCountries, arguments) 46 | } 47 | 48 | export function getCountryCallingCode() { 49 | return call(_getCountryCallingCode, arguments) 50 | } 51 | 52 | export function isSupportedCountry() { 53 | return call(_isSupportedCountry, arguments) 54 | } -------------------------------------------------------------------------------- /mobile/index.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/mobile/metadata' 2 | 3 | import { 4 | parsePhoneNumber as _parsePhoneNumber, 5 | formatPhoneNumber as _formatPhoneNumber, 6 | formatPhoneNumberIntl as _formatPhoneNumberIntl, 7 | isValidPhoneNumber as _isValidPhoneNumber, 8 | isPossiblePhoneNumber as _isPossiblePhoneNumber, 9 | getCountries as _getCountries, 10 | getCountryCallingCode as _getCountryCallingCode, 11 | isSupportedCountry as _isSupportedCountry 12 | } from '../core/index.js' 13 | 14 | import { createPhoneInput } from '../modules/PhoneInputWithCountryDefault.js' 15 | 16 | function call(func, _arguments) { 17 | var args = Array.prototype.slice.call(_arguments) 18 | args.push(metadata) 19 | return func.apply(this, args) 20 | } 21 | 22 | export default createPhoneInput(metadata) 23 | 24 | export function parsePhoneNumber() { 25 | return call(_parsePhoneNumber, arguments) 26 | } 27 | 28 | export function formatPhoneNumber() { 29 | return call(_formatPhoneNumber, arguments) 30 | } 31 | 32 | export function formatPhoneNumberIntl() { 33 | return call(_formatPhoneNumberIntl, arguments) 34 | } 35 | 36 | export function isValidPhoneNumber() { 37 | return call(_isValidPhoneNumber, arguments) 38 | } 39 | 40 | export function isPossiblePhoneNumber() { 41 | return call(_isPossiblePhoneNumber, arguments) 42 | } 43 | 44 | export function getCountries() { 45 | return call(_getCountries, arguments) 46 | } 47 | 48 | export function getCountryCallingCode() { 49 | return call(_getCountryCallingCode, arguments) 50 | } 51 | 52 | export function isSupportedCountry() { 53 | return call(_isSupportedCountry, arguments) 54 | } -------------------------------------------------------------------------------- /test/exports.max.test.js: -------------------------------------------------------------------------------- 1 | import PhoneInput, { 2 | parsePhoneNumber, 3 | formatPhoneNumber, 4 | formatPhoneNumberIntl, 5 | isValidPhoneNumber, 6 | isPossiblePhoneNumber, 7 | getCountryCallingCode, 8 | getCountries, 9 | isSupportedCountry 10 | } from '../max/index.js' 11 | 12 | import Library from '../max/index.cjs' 13 | 14 | describe('exports/max', () => { 15 | it('should export ES6', () => { 16 | PhoneInput.render.should.be.a('function') 17 | parsePhoneNumber('+78005553535').country.should.equal('RU') 18 | formatPhoneNumber('+12133734253').should.equal('(213) 373-4253') 19 | formatPhoneNumberIntl('+12133734253').should.equal('+1 213 373 4253') 20 | isValidPhoneNumber('+12133734253').should.equal(true) 21 | isPossiblePhoneNumber('+19999999999').should.equal(true) 22 | getCountryCallingCode('US').should.equal('1') 23 | getCountries()[0].length.should.equal(2) 24 | isSupportedCountry('XX').should.equal(false) 25 | }) 26 | 27 | it('should export CommonJS', () => { 28 | Library.render.should.be.a('function') 29 | Library.default.render.should.be.a('function') 30 | Library.parsePhoneNumber('+78005553535').country.should.equal('RU') 31 | Library.formatPhoneNumber('+12133734253').should.equal('(213) 373-4253') 32 | Library.formatPhoneNumberIntl('+12133734253').should.equal('+1 213 373 4253') 33 | Library.isValidPhoneNumber('+12133734253').should.equal(true) 34 | Library.isPossiblePhoneNumber('+19999999999').should.equal(true) 35 | Library.getCountryCallingCode('US').should.equal('1') 36 | Library.getCountries()[0].length.should.equal(2) 37 | Library.isSupportedCountry('XX').should.equal(false) 38 | }) 39 | }) -------------------------------------------------------------------------------- /test/exports.min.test.js: -------------------------------------------------------------------------------- 1 | import PhoneInput, { 2 | parsePhoneNumber, 3 | formatPhoneNumber, 4 | formatPhoneNumberIntl, 5 | isValidPhoneNumber, 6 | isPossiblePhoneNumber, 7 | getCountryCallingCode, 8 | getCountries, 9 | isSupportedCountry 10 | } from '../min/index.js' 11 | 12 | import Library from '../min/index.cjs' 13 | 14 | describe('exports/min', () => { 15 | it('should export ES6', () => { 16 | PhoneInput.render.should.be.a('function') 17 | parsePhoneNumber('+78005553535').country.should.equal('RU') 18 | formatPhoneNumber('+12133734253').should.equal('(213) 373-4253') 19 | formatPhoneNumberIntl('+12133734253').should.equal('+1 213 373 4253') 20 | isValidPhoneNumber('+12133734253').should.equal(true) 21 | isPossiblePhoneNumber('+19999999999').should.equal(true) 22 | getCountryCallingCode('US').should.equal('1') 23 | getCountries()[0].length.should.equal(2) 24 | isSupportedCountry('XX').should.equal(false) 25 | }) 26 | 27 | it('should export CommonJS', () => { 28 | Library.render.should.be.a('function') 29 | Library.default.render.should.be.a('function') 30 | Library.parsePhoneNumber('+78005553535').country.should.equal('RU') 31 | Library.formatPhoneNumber('+12133734253').should.equal('(213) 373-4253') 32 | Library.formatPhoneNumberIntl('+12133734253').should.equal('+1 213 373 4253') 33 | Library.isValidPhoneNumber('+12133734253').should.equal(true) 34 | Library.isPossiblePhoneNumber('+19999999999').should.equal(true) 35 | Library.getCountryCallingCode('US').should.equal('1') 36 | Library.getCountries()[0].length.should.equal(2) 37 | Library.isSupportedCountry('XX').should.equal(false) 38 | }) 39 | }) -------------------------------------------------------------------------------- /test/exports.input.test.js: -------------------------------------------------------------------------------- 1 | import PhoneInput, { 2 | parsePhoneNumber, 3 | formatPhoneNumber, 4 | formatPhoneNumberIntl, 5 | isValidPhoneNumber, 6 | isPossiblePhoneNumber, 7 | getCountryCallingCode, 8 | getCountries, 9 | isSupportedCountry 10 | } from '../input/index.js' 11 | 12 | import Library from '../input/index.cjs' 13 | 14 | describe('exports/input', () => { 15 | it('should export ES6', () => { 16 | PhoneInput.render.should.be.a('function') 17 | parsePhoneNumber('+78005553535').country.should.equal('RU') 18 | formatPhoneNumber('+12133734253').should.equal('(213) 373-4253') 19 | formatPhoneNumberIntl('+12133734253').should.equal('+1 213 373 4253') 20 | isValidPhoneNumber('+12133734253').should.equal(true) 21 | isPossiblePhoneNumber('+19999999999').should.equal(true) 22 | getCountryCallingCode('US').should.equal('1') 23 | getCountries()[0].length.should.equal(2) 24 | isSupportedCountry('XX').should.equal(false) 25 | }) 26 | 27 | it('should export CommonJS', () => { 28 | Library.render.should.be.a('function') 29 | Library.default.render.should.be.a('function') 30 | Library.parsePhoneNumber('+78005553535').country.should.equal('RU') 31 | Library.formatPhoneNumber('+12133734253').should.equal('(213) 373-4253') 32 | Library.formatPhoneNumberIntl('+12133734253').should.equal('+1 213 373 4253') 33 | Library.isValidPhoneNumber('+12133734253').should.equal(true) 34 | Library.isPossiblePhoneNumber('+19999999999').should.equal(true) 35 | Library.getCountryCallingCode('US').should.equal('1') 36 | Library.getCountries()[0].length.should.equal(2) 37 | Library.isSupportedCountry('XX').should.equal(false) 38 | }) 39 | }) -------------------------------------------------------------------------------- /test/exports.mobile.test.js: -------------------------------------------------------------------------------- 1 | import PhoneInput, { 2 | parsePhoneNumber, 3 | formatPhoneNumber, 4 | formatPhoneNumberIntl, 5 | isValidPhoneNumber, 6 | isPossiblePhoneNumber, 7 | getCountryCallingCode, 8 | getCountries, 9 | isSupportedCountry 10 | } from '../mobile/index.js' 11 | 12 | import Library from '../mobile/index.cjs' 13 | 14 | describe('exports/mobile', () => { 15 | it('should export ES6', () => { 16 | PhoneInput.render.should.be.a('function') 17 | parsePhoneNumber('+33109758351').country.should.equal('FR') 18 | formatPhoneNumber('+12133734253').should.equal('(213) 373-4253') 19 | formatPhoneNumberIntl('+12133734253').should.equal('+1 213 373 4253') 20 | isValidPhoneNumber('+12133734253').should.equal(true) 21 | isPossiblePhoneNumber('+19999999999').should.equal(true) 22 | getCountryCallingCode('US').should.equal('1') 23 | getCountries()[0].length.should.equal(2) 24 | isSupportedCountry('XX').should.equal(false) 25 | }) 26 | 27 | it('should export CommonJS', () => { 28 | Library.render.should.be.a('function') 29 | Library.default.render.should.be.a('function') 30 | Library.parsePhoneNumber('+33109758351').country.should.equal('FR') 31 | Library.formatPhoneNumber('+12133734253').should.equal('(213) 373-4253') 32 | Library.formatPhoneNumberIntl('+12133734253').should.equal('+1 213 373 4253') 33 | Library.isValidPhoneNumber('+12133734253').should.equal(true) 34 | Library.isPossiblePhoneNumber('+19999999999').should.equal(true) 35 | Library.getCountryCallingCode('US').should.equal('1') 36 | Library.getCountries()[0].length.should.equal(2) 37 | Library.isSupportedCountry('XX').should.equal(false) 38 | }) 39 | }) -------------------------------------------------------------------------------- /source/PhoneInputBrowser.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | import PhoneInput_ from './PhoneInput.js' 5 | import InputSmart from './InputSmart.js' 6 | import InputBasic from './InputBasic.js' 7 | 8 | export function createInput(defaultMetadata) { 9 | function PhoneInput({ 10 | // ``. 11 | type = 'tel', 12 | // Remember (and autofill) the value as a phone number. 13 | autoComplete = 'tel', 14 | smartCaret = true, 15 | metadata = defaultMetadata, 16 | ...rest 17 | }, ref) { 18 | return ( 19 | 27 | ) 28 | } 29 | 30 | PhoneInput = React.forwardRef(PhoneInput) 31 | 32 | PhoneInput.propTypes = { 33 | /** 34 | * HTML `` `type` attribute. 35 | */ 36 | type: PropTypes.string, 37 | 38 | /** 39 | * HTML `` `autocomplete` attribute. 40 | */ 41 | autoComplete: PropTypes.string, 42 | 43 | /** 44 | * By default, the caret position is being "intelligently" managed 45 | * while a user inputs a phone number. 46 | * This "smart" caret behavior can be turned off 47 | * by passing `smartCaret={false}` property. 48 | * This is just an "escape hatch" for any possible caret position issues. 49 | */ 50 | // Is `true` by default. 51 | smartCaret: PropTypes.bool, 52 | 53 | /** 54 | * `libphonenumber-js` metadata. 55 | */ 56 | metadata: PropTypes.object 57 | } 58 | 59 | return PhoneInput 60 | } 61 | 62 | export default createInput() -------------------------------------------------------------------------------- /source/Flag.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import classNames from 'classnames' 4 | 5 | // Default country flag icon. 6 | // `` is wrapped in a `
` to prevent SVGs from exploding in size in IE 11. 7 | // https://github.com/catamphetamine/react-phone-number-input/issues/111 8 | export default function FlagComponent({ 9 | country, 10 | countryName, 11 | flags, 12 | flagUrl, 13 | ...rest 14 | }) { 15 | if (flags && flags[country]) { 16 | return flags[country]({ title: countryName }) 17 | } 18 | return ( 19 | {countryName} 24 | ) 25 | } 26 | 27 | FlagComponent.propTypes = { 28 | // The country to be selected by default. 29 | // Two-letter country code ("ISO 3166-1 alpha-2"). 30 | country: PropTypes.string.isRequired, 31 | 32 | // Will be HTML `title` attribute of the ``. 33 | countryName: PropTypes.string.isRequired, 34 | 35 | // Country flag icon components. 36 | // By default flag icons are inserted as ``s 37 | // with their `src` pointed to `country-flag-icons` gitlab pages website. 38 | // There might be cases (e.g. an offline application) 39 | // where having a large (3 megabyte) `` flags 40 | // bundle is more appropriate. 41 | // `import flags from 'react-phone-number-input/flags'`. 42 | flags: PropTypes.objectOf(PropTypes.elementType), 43 | 44 | // A URL for a country flag icon. 45 | // By default it points to `country-flag-icons` gitlab pages website. 46 | flagUrl: PropTypes.string.isRequired 47 | } 48 | -------------------------------------------------------------------------------- /react-hook-form-core/index.d.ts: -------------------------------------------------------------------------------- 1 | // React TypeScript Cheatsheet doesn't recommend using `React.FunctionalComponent` (`React.FC`). 2 | // https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components 3 | 4 | import * as React from 'react'; 5 | 6 | import { FieldValues } from 'react-hook-form'; 7 | 8 | import { 9 | Metadata, 10 | Labels, 11 | DefaultInputComponentProps 12 | } from '../index.d.js'; 13 | 14 | export { 15 | Country, 16 | Value 17 | } from '../index.d.js'; 18 | 19 | import { 20 | Props as BaseProps, 21 | DefaultFormValues 22 | } from '../react-hook-form/index.d.js'; 23 | 24 | type Props = BaseProps & { 25 | metadata: Metadata; 26 | labels: Labels; 27 | } 28 | 29 | type PhoneInputWithCountrySelectType = (props: Props) => JSX.Element; 30 | 31 | // Could also export the component that would accept custom "generics", 32 | // but seems like it would also introduce some inconvenience when using `typeof PhoneInputWithCountrySelect` 33 | // for defining the type of the `props`. 34 | // https://github.com/catamphetamine/react-phone-number-input/issues/414#issuecomment-1220679025 35 | // type PhoneInputWithCountrySelectType = (props: Props) => JSX.Element; 36 | 37 | declare const PhoneInputWithCountrySelect: PhoneInputWithCountrySelectType; 38 | 39 | export default PhoneInputWithCountrySelect; -------------------------------------------------------------------------------- /react-native-input/index.d.ts: -------------------------------------------------------------------------------- 1 | // React TypeScript Cheatsheet doesn't recommend using `React.FunctionalComponent`. 2 | // https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components 3 | 4 | import * as React from 'react'; 5 | 6 | import { 7 | Value, 8 | ExternalValue, 9 | Metadata 10 | } from '../index.d.js'; 11 | 12 | export { 13 | Country, 14 | Value 15 | } from '../index.d.js'; 16 | 17 | import { 18 | PropsWithoutSmartCaret 19 | } from '../input/index.d.js'; 20 | 21 | // The default React.Native input component accepts properties: 22 | // * `value: string` 23 | // * `onChangeText(value: string): void` 24 | // * Any other React.Native-specific input component properties 25 | type UnderlyingInputComponentProps = Omit & { 26 | value?: Value | ExternalValue; 27 | onChangeText(value: Value): void; 28 | }; 29 | 30 | type Props = PropsWithoutSmartCaret> & { 31 | metadata?: Metadata; 32 | }; 33 | 34 | // In an HTML DOM environment, there's 35 | // `React.InputHTMLAttributes` type available. 36 | // In a React Native environment, there seems to be no such equivalent. 37 | // Hence, using a `[anyProperty: string]: any` workaround 38 | // for supporting any "other" properties that get passed through 39 | // to the input component. 40 | type DefaultInputComponentProps = { 41 | [anyProperty: string]: any; 42 | } 43 | 44 | type PhoneInputComponentType = React.ForwardRefExoticComponent & React.RefAttributes> 45 | 46 | declare const PhoneInput: PhoneInputComponentType; 47 | 48 | export default PhoneInput; -------------------------------------------------------------------------------- /input/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var metadata = require('libphonenumber-js/min/metadata') 9 | var core = require('../core/index.cjs') 10 | var createInput = require('../commonjs/PhoneInputBrowser.js').createInput 11 | 12 | function call(func, _arguments) { 13 | var args = Array.prototype.slice.call(_arguments) 14 | args.push(metadata) 15 | return func.apply(this, args) 16 | } 17 | 18 | var PhoneInput = createInput(metadata) 19 | 20 | exports = module.exports = PhoneInput 21 | 22 | exports.parsePhoneNumber = function parsePhoneNumber() { 23 | return call(core.parsePhoneNumber, arguments) 24 | } 25 | 26 | exports.formatPhoneNumber = function formatPhoneNumber() { 27 | return call(core.formatPhoneNumber, arguments) 28 | } 29 | 30 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 31 | return call(core.formatPhoneNumberIntl, arguments) 32 | } 33 | 34 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 35 | return call(core.isValidPhoneNumber, arguments) 36 | } 37 | 38 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 39 | return call(core.isPossiblePhoneNumber, arguments) 40 | } 41 | 42 | exports.getCountries = function getCountries() { 43 | return call(core.getCountries, arguments) 44 | } 45 | 46 | exports.getCountryCallingCode = function getCountryCallingCode() { 47 | return call(core.getCountryCallingCode, arguments) 48 | } 49 | 50 | exports.isSupportedCountry = function isSupportedCountry() { 51 | return call(core.isSupportedCountry, arguments) 52 | } 53 | 54 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /source/libphonenumber/formatPhoneNumber.test.js: -------------------------------------------------------------------------------- 1 | import _formatPhoneNumber, { formatPhoneNumberIntl as _formatPhoneNumberIntl } from './formatPhoneNumber.js' 2 | import metadata from 'libphonenumber-js/min/metadata' 3 | 4 | function call(func, _arguments) { 5 | var args = Array.prototype.slice.call(_arguments) 6 | args.push(metadata) 7 | return func.apply(this, args) 8 | } 9 | 10 | function formatPhoneNumber() { 11 | return call(_formatPhoneNumber, arguments) 12 | } 13 | 14 | function formatPhoneNumberIntl() { 15 | return call(_formatPhoneNumberIntl, arguments) 16 | } 17 | 18 | describe('formatPhoneNumber', () => { 19 | it('should format phone numbers', () => { 20 | expect(() => formatPhoneNumber()).to.throw('must be a string') 21 | // formatPhoneNumber().should.equal('') 22 | formatPhoneNumber(null).should.equal('') 23 | formatPhoneNumber('').should.equal('') 24 | expect(() => _formatPhoneNumber('+1', 'NATIONAL')).to.throw('`metadata` argument not passed') 25 | expect(() => _formatPhoneNumber('+12133734253', undefined, metadata)).to.throw('Unknown "format"') 26 | expect(() => _formatPhoneNumber('+12133734253', '123', metadata)).to.throw('Unknown "format"') 27 | formatPhoneNumber('+1', 'NATIONAL').should.equal('') 28 | formatPhoneNumber('+12133734253', 'NATIONAL').should.equal('(213) 373-4253') 29 | formatPhoneNumber('+12133734253').should.equal('(213) 373-4253') 30 | formatPhoneNumber('+12133734253', 'INTERNATIONAL').should.equal('+1 213 373 4253') 31 | // Deprecated. 32 | // Legacy `format`s. 33 | formatPhoneNumber('+12133734253', 'National').should.equal('(213) 373-4253') 34 | formatPhoneNumber('+12133734253', 'International').should.equal('+1 213 373 4253') 35 | }) 36 | 37 | it('should format international phone numbers', () => { 38 | formatPhoneNumberIntl('+12133734253').should.equal('+1 213 373 4253') 39 | }) 40 | }) -------------------------------------------------------------------------------- /input-max/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var metadata = require('libphonenumber-js/max/metadata') 9 | var core = require('../core/index.cjs') 10 | var createInput = require('../commonjs/PhoneInputBrowser.js').createInput 11 | 12 | function call(func, _arguments) { 13 | var args = Array.prototype.slice.call(_arguments) 14 | args.push(metadata) 15 | return func.apply(this, args) 16 | } 17 | 18 | var PhoneInput = createInput(metadata) 19 | 20 | exports = module.exports = PhoneInput 21 | 22 | exports.parsePhoneNumber = function parsePhoneNumber() { 23 | return call(core.parsePhoneNumber, arguments) 24 | } 25 | 26 | exports.formatPhoneNumber = function formatPhoneNumber() { 27 | return call(core.formatPhoneNumber, arguments) 28 | } 29 | 30 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 31 | return call(core.formatPhoneNumberIntl, arguments) 32 | } 33 | 34 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 35 | return call(core.isValidPhoneNumber, arguments) 36 | } 37 | 38 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 39 | return call(core.isPossiblePhoneNumber, arguments) 40 | } 41 | 42 | exports.getCountries = function getCountries() { 43 | return call(core.getCountries, arguments) 44 | } 45 | 46 | exports.getCountryCallingCode = function getCountryCallingCode() { 47 | return call(core.getCountryCallingCode, arguments) 48 | } 49 | 50 | exports.isSupportedCountry = function isSupportedCountry() { 51 | return call(core.isSupportedCountry, arguments) 52 | } 53 | 54 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /react-hook-form-input/index.d.ts: -------------------------------------------------------------------------------- 1 | // React TypeScript Cheatsheet doesn't recommend using `React.FunctionalComponent` (`React.FC`). 2 | // https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components 3 | 4 | import * as React from 'react'; 5 | 6 | import { FieldValues } from 'react-hook-form'; 7 | 8 | import { 9 | ReactHookFormComponentProps, 10 | DefaultFormValues 11 | } from '../react-hook-form/index.d.js'; 12 | 13 | import { 14 | FeatureProps as BaseProps, 15 | } from '../input/index.d.js'; 16 | 17 | import { 18 | DefaultInputComponentProps 19 | } from '../index.d.js'; 20 | 21 | export { 22 | Country, 23 | Value 24 | } from '../index.d.js'; 25 | 26 | // `Props` are used in: 27 | // * `react-hook-form-input-core/index.d.ts` 28 | export type Props = BaseProps & ReactHookFormComponentProps & { 29 | // onChange?(event: React.ChangeEvent): void; 30 | // onBlur?(event: React.FocusEvent): void; 31 | } 32 | 33 | type PhoneInputType = (props: Props) => JSX.Element; 34 | 35 | // Could also export the component that would accept custom "generics", 36 | // but seems like it would also introduce some inconvenience when using `typeof PhoneInput` 37 | // for defining the type of the `props`. 38 | // https://github.com/catamphetamine/react-phone-number-input/issues/414#issuecomment-1220679025 39 | // type PhoneInputType = (props: Props) => JSX.Element; 40 | 41 | declare const PhoneInput: PhoneInputType; 42 | 43 | export default PhoneInput; -------------------------------------------------------------------------------- /input-mobile/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var metadata = require('libphonenumber-js/mobile/metadata') 9 | var core = require('../core/index.cjs') 10 | var createInput = require('../commonjs/PhoneInputBrowser.js').createInput 11 | 12 | function call(func, _arguments) { 13 | var args = Array.prototype.slice.call(_arguments) 14 | args.push(metadata) 15 | return func.apply(this, args) 16 | } 17 | 18 | var PhoneInput = createInput(metadata) 19 | 20 | exports = module.exports = PhoneInput 21 | 22 | exports.parsePhoneNumber = function parsePhoneNumber() { 23 | return call(core.parsePhoneNumber, arguments) 24 | } 25 | 26 | exports.formatPhoneNumber = function formatPhoneNumber() { 27 | return call(core.formatPhoneNumber, arguments) 28 | } 29 | 30 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 31 | return call(core.formatPhoneNumberIntl, arguments) 32 | } 33 | 34 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 35 | return call(core.isValidPhoneNumber, arguments) 36 | } 37 | 38 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 39 | return call(core.isPossiblePhoneNumber, arguments) 40 | } 41 | 42 | exports.getCountries = function getCountries() { 43 | return call(core.getCountries, arguments) 44 | } 45 | 46 | exports.getCountryCallingCode = function getCountryCallingCode() { 47 | return call(core.getCountryCallingCode, arguments) 48 | } 49 | 50 | exports.isSupportedCountry = function isSupportedCountry() { 51 | return call(core.isSupportedCountry, arguments) 52 | } 53 | 54 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /test/exports.core.test.js: -------------------------------------------------------------------------------- 1 | import PhoneInput, { 2 | parsePhoneNumber, 3 | formatPhoneNumber, 4 | formatPhoneNumberIntl, 5 | isValidPhoneNumber, 6 | isPossiblePhoneNumber, 7 | getCountryCallingCode, 8 | getCountries, 9 | isSupportedCountry 10 | } from '../core/index.js' 11 | 12 | import Library from '../core/index.cjs' 13 | 14 | import metadata from 'libphonenumber-js/min/metadata' 15 | 16 | describe('exports/core', () => { 17 | it('should export ES6', () => { 18 | PhoneInput.render.should.be.a('function') 19 | parsePhoneNumber('+78005553535', metadata).country.should.equal('RU') 20 | formatPhoneNumber('+12133734253', metadata).should.equal('(213) 373-4253') 21 | formatPhoneNumberIntl('+12133734253', metadata).should.equal('+1 213 373 4253') 22 | isValidPhoneNumber('+12133734253', metadata).should.equal(true) 23 | isPossiblePhoneNumber('+19999999999', metadata).should.equal(true) 24 | getCountryCallingCode('US', metadata).should.equal('1') 25 | getCountries(metadata)[0].length.should.equal(2) 26 | isSupportedCountry('XX', metadata).should.equal(false) 27 | }) 28 | 29 | it('should export CommonJS', () => { 30 | Library.render.should.be.a('function') 31 | Library.default.render.should.be.a('function') 32 | Library.parsePhoneNumber('+78005553535', metadata).country.should.equal('RU') 33 | Library.formatPhoneNumber('+12133734253', metadata).should.equal('(213) 373-4253') 34 | Library.formatPhoneNumberIntl('+12133734253', metadata).should.equal('+1 213 373 4253') 35 | Library.isValidPhoneNumber('+12133734253', metadata).should.equal(true) 36 | Library.isPossiblePhoneNumber('+19999999999', metadata).should.equal(true) 37 | Library.getCountryCallingCode('US', metadata).should.equal('1') 38 | Library.getCountries(metadata)[0].length.should.equal(2) 39 | Library.isSupportedCountry('XX', metadata).should.equal(false) 40 | }) 41 | }) -------------------------------------------------------------------------------- /max/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var metadata = require('libphonenumber-js/max/metadata') 9 | var core = require('../core/index.cjs') 10 | var createPhoneInput = require('../commonjs/PhoneInputWithCountryDefault.js').createPhoneInput 11 | 12 | function call(func, _arguments) { 13 | var args = Array.prototype.slice.call(_arguments) 14 | args.push(metadata) 15 | return func.apply(this, args) 16 | } 17 | 18 | var PhoneInput = createPhoneInput(metadata) 19 | 20 | exports = module.exports = PhoneInput 21 | 22 | exports.parsePhoneNumber = function parsePhoneNumber() { 23 | return call(core.parsePhoneNumber, arguments) 24 | } 25 | 26 | exports.formatPhoneNumber = function formatPhoneNumber() { 27 | return call(core.formatPhoneNumber, arguments) 28 | } 29 | 30 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 31 | return call(core.formatPhoneNumberIntl, arguments) 32 | } 33 | 34 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 35 | return call(core.isValidPhoneNumber, arguments) 36 | } 37 | 38 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 39 | return call(core.isPossiblePhoneNumber, arguments) 40 | } 41 | 42 | exports.getCountries = function getCountries() { 43 | return call(core.getCountries, arguments) 44 | } 45 | 46 | exports.getCountryCallingCode = function getCountryCallingCode() { 47 | return call(core.getCountryCallingCode, arguments) 48 | } 49 | 50 | exports.isSupportedCountry = function isSupportedCountry() { 51 | return call(core.isSupportedCountry, arguments) 52 | } 53 | 54 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /min/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var metadata = require('libphonenumber-js/min/metadata') 9 | var core = require('../core/index.cjs') 10 | var createPhoneInput = require('../commonjs/PhoneInputWithCountryDefault.js').createPhoneInput 11 | 12 | function call(func, _arguments) { 13 | var args = Array.prototype.slice.call(_arguments) 14 | args.push(metadata) 15 | return func.apply(this, args) 16 | } 17 | 18 | var PhoneInput = createPhoneInput(metadata) 19 | 20 | exports = module.exports = PhoneInput 21 | 22 | exports.parsePhoneNumber = function parsePhoneNumber() { 23 | return call(core.parsePhoneNumber, arguments) 24 | } 25 | 26 | exports.formatPhoneNumber = function formatPhoneNumber() { 27 | return call(core.formatPhoneNumber, arguments) 28 | } 29 | 30 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 31 | return call(core.formatPhoneNumberIntl, arguments) 32 | } 33 | 34 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 35 | return call(core.isValidPhoneNumber, arguments) 36 | } 37 | 38 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 39 | return call(core.isPossiblePhoneNumber, arguments) 40 | } 41 | 42 | exports.getCountries = function getCountries() { 43 | return call(core.getCountries, arguments) 44 | } 45 | 46 | exports.getCountryCallingCode = function getCountryCallingCode() { 47 | return call(core.getCountryCallingCode, arguments) 48 | } 49 | 50 | exports.isSupportedCountry = function isSupportedCountry() { 51 | return call(core.isSupportedCountry, arguments) 52 | } 53 | 54 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /mobile/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var metadata = require('libphonenumber-js/mobile/metadata') 9 | var core = require('../core/index.cjs') 10 | var createPhoneInput = require('../commonjs/PhoneInputWithCountryDefault.js').createPhoneInput 11 | 12 | function call(func, _arguments) { 13 | var args = Array.prototype.slice.call(_arguments) 14 | args.push(metadata) 15 | return func.apply(this, args) 16 | } 17 | 18 | var PhoneInput = createPhoneInput(metadata) 19 | 20 | exports = module.exports = PhoneInput 21 | 22 | exports.parsePhoneNumber = function parsePhoneNumber() { 23 | return call(core.parsePhoneNumber, arguments) 24 | } 25 | 26 | exports.formatPhoneNumber = function formatPhoneNumber() { 27 | return call(core.formatPhoneNumber, arguments) 28 | } 29 | 30 | exports.formatPhoneNumberIntl = function formatPhoneNumberIntl() { 31 | return call(core.formatPhoneNumberIntl, arguments) 32 | } 33 | 34 | exports.isValidPhoneNumber = function isValidPhoneNumber() { 35 | return call(core.isValidPhoneNumber, arguments) 36 | } 37 | 38 | exports.isPossiblePhoneNumber = function isPossiblePhoneNumber() { 39 | return call(core.isPossiblePhoneNumber, arguments) 40 | } 41 | 42 | exports.getCountries = function getCountries() { 43 | return call(core.getCountries, arguments) 44 | } 45 | 46 | exports.getCountryCallingCode = function getCountryCallingCode() { 47 | return call(core.getCountryCallingCode, arguments) 48 | } 49 | 50 | exports.isSupportedCountry = function isSupportedCountry() { 51 | return call(core.isSupportedCountry, arguments) 52 | } 53 | 54 | exports['default'] = PhoneInput -------------------------------------------------------------------------------- /scripts/fix-locales.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | import { getCountries, isSupportedCountry } from 'libphonenumber-js/min' 4 | 5 | import en from '../locale/en.json' with { type: 'json' } 6 | 7 | const countries = Object.keys(en).filter(_ => _.length === 2 && _.toUpperCase() === _) 8 | countries.sort() 9 | 10 | const nonCountries = Object.keys(en).filter(_ => countries.indexOf(_) < 0) 11 | 12 | // Check that all `libphonenumber-js` countries have labels. 13 | for (const country of getCountries()) { 14 | if (!countries.includes(country)) { 15 | throw new Error(`"${country}" country is missing from messages`) 16 | } 17 | } 18 | 19 | // For each locale. 20 | fs.readdirSync(path.resolve('./locale')).filter(name => name.endsWith('.json')).map((name) => { 21 | if (name === 'en.json') { 22 | return 23 | } 24 | // Read locale data. 25 | const locale = readJsonFromFile(`./locale/${name}`) 26 | // Add missing countries. 27 | // Remove non-existing countries. 28 | // Re-sort locale data keys. 29 | const newLocale = {} 30 | for (const nonCountry of nonCountries) { 31 | if (locale[nonCountry]) { 32 | newLocale[nonCountry] = locale[nonCountry] 33 | } else { 34 | console.log(`"${name}" was missing "${nonCountry}" key. Substituted with "${en[nonCountry]}".`) 35 | newLocale[nonCountry] = en[nonCountry] 36 | } 37 | } 38 | for (const country of countries) { 39 | if (locale[country]) { 40 | newLocale[country] = locale[country] 41 | } else { 42 | console.log(`"${name}" was missing "${country}" country. Substituted with "${en[country]}".`) 43 | newLocale[country] = en[country] 44 | } 45 | } 46 | // Output locale data. 47 | fs.writeFileSync(path.resolve(`./locale/${name}`), JSON.stringify(newLocale, null, '\t'), 'utf-8') 48 | }) 49 | 50 | function readJsonFromFile(path) { 51 | return JSON.parse(fs.readFileSync(path, 'utf8')) 52 | } 53 | -------------------------------------------------------------------------------- /test/exports.input-core.test.js: -------------------------------------------------------------------------------- 1 | import PhoneInput, { 2 | parsePhoneNumber, 3 | formatPhoneNumber, 4 | formatPhoneNumberIntl, 5 | isValidPhoneNumber, 6 | isPossiblePhoneNumber, 7 | getCountryCallingCode, 8 | getCountries, 9 | isSupportedCountry 10 | } from '../input-core/index.js' 11 | 12 | import Library from '../input-core/index.cjs' 13 | 14 | import metadata from 'libphonenumber-js/min/metadata' 15 | 16 | describe('exports/input-core', () => { 17 | it('should export ES6', () => { 18 | PhoneInput.render.should.be.a('function') 19 | parsePhoneNumber('+78005553535', metadata).country.should.equal('RU') 20 | formatPhoneNumber('+12133734253', metadata).should.equal('(213) 373-4253') 21 | formatPhoneNumberIntl('+12133734253', metadata).should.equal('+1 213 373 4253') 22 | isValidPhoneNumber('+12133734253', metadata).should.equal(true) 23 | isPossiblePhoneNumber('+19999999999', metadata).should.equal(true) 24 | getCountryCallingCode('US', metadata).should.equal('1') 25 | getCountries(metadata)[0].length.should.equal(2) 26 | isSupportedCountry('XX', metadata).should.equal(false) 27 | }) 28 | 29 | it('should export CommonJS', () => { 30 | Library.render.should.be.a('function') 31 | Library.default.render.should.be.a('function') 32 | Library.parsePhoneNumber('+78005553535', metadata).country.should.equal('RU') 33 | Library.formatPhoneNumber('+12133734253', metadata).should.equal('(213) 373-4253') 34 | Library.formatPhoneNumberIntl('+12133734253', metadata).should.equal('+1 213 373 4253') 35 | Library.isValidPhoneNumber('+12133734253', metadata).should.equal(true) 36 | Library.isPossiblePhoneNumber('+19999999999', metadata).should.equal(true) 37 | Library.getCountryCallingCode('US', metadata).should.equal('1') 38 | Library.getCountries(metadata)[0].length.should.equal(2) 39 | Library.isSupportedCountry('XX', metadata).should.equal(false) 40 | }) 41 | }) -------------------------------------------------------------------------------- /source/useInputKeyDownHandler.js: -------------------------------------------------------------------------------- 1 | import { useCallback } from 'react' 2 | 3 | // Returns a custom `onKeyDown` handler that works around a Backspace keypress edge case: 4 | // * `` 5 | // * When placing the caret before the leading plus character and pressing Backspace, 6 | // it duplicates the country calling code in the ``. 7 | // https://github.com/catamphetamine/react-phone-number-input/issues/442 8 | export default function useInputKeyDownHandler({ 9 | onKeyDown, 10 | inputFormat 11 | }) { 12 | return useCallback((event) => { 13 | // Usability: 14 | // Don't allow the user to erase a leading "+" character when "international" input mode is forced. 15 | // That indicates to the user that they can't possibly enter the phone number in a non-international format. 16 | if (event.keyCode === BACKSPACE_KEY_CODE && inputFormat === 'INTERNATIONAL') { 17 | // It checks `event.target` here for being an `` element 18 | // because "keydown" events may bubble from arbitrary child elements 19 | // so there's no guarantee that `event.target` represents an `` element. 20 | // Also, since `inputComponent` is not neceesarily an ``, this check is required too. 21 | if (event.target instanceof HTMLInputElement) { 22 | if (getCaretPosition(event.target) === LEADING_PLUS.length) { 23 | event.preventDefault() 24 | return 25 | } 26 | } 27 | } 28 | if (onKeyDown) { 29 | onKeyDown(event) 30 | } 31 | }, [ 32 | onKeyDown, 33 | inputFormat 34 | ]) 35 | } 36 | 37 | // Gets the caret position in an `` field. 38 | // The caret position starts with `0` which means "before the first character". 39 | function getCaretPosition(element) { 40 | return element.selectionStart 41 | } 42 | 43 | const BACKSPACE_KEY_CODE = 8 44 | 45 | const LEADING_PLUS = '+' -------------------------------------------------------------------------------- /source/CountryIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import classNames from 'classnames' 4 | 5 | import DefaultInternationalIcon from './InternationalIcon.js' 6 | import Flag from './Flag.js' 7 | 8 | export function createCountryIconComponent({ 9 | flags, 10 | flagUrl, 11 | flagComponent: FlagComponent, 12 | internationalIcon: InternationalIcon 13 | }) { 14 | function CountryIcon({ 15 | country, 16 | label, 17 | aspectRatio, 18 | ...rest 19 | }) { 20 | // `aspectRatio` is currently a hack for the default "International" icon 21 | // to render it as a square when Unicode flag icons are used. 22 | // So `aspectRatio` property is only used with the default "International" icon. 23 | const _aspectRatio = InternationalIcon === DefaultInternationalIcon ? aspectRatio : undefined 24 | return ( 25 |
31 | { 32 | country 33 | ? 34 | 40 | : 41 | 45 | } 46 |
47 | ) 48 | } 49 | 50 | CountryIcon.propTypes = { 51 | country: PropTypes.string, 52 | label: PropTypes.string.isRequired, 53 | aspectRatio: PropTypes.number 54 | } 55 | 56 | return CountryIcon 57 | } 58 | 59 | export default createCountryIconComponent({ 60 | // Must be equal to `defaultProps.flagUrl` in `./PhoneInputWithCountry.js`. 61 | flagUrl: 'https://purecatamphetamine.github.io/country-flag-icons/3x2/{XX}.svg', 62 | flagComponent: Flag, 63 | internationalIcon: DefaultInternationalIcon 64 | }) -------------------------------------------------------------------------------- /source/react-native/PhoneTextInput.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { TextInput } from 'react-native' 4 | 5 | /** 6 | * This is an _experimental_ React Native component. 7 | * Feedback thread: https://github.com/catamphetamine/react-phone-number-input/issues/296 8 | */ 9 | function PhoneTextInput({ 10 | onChange, 11 | // By default, shows phone number suggestion(s) when the user focuses the input field. 12 | autoCompleteType = 'tel', 13 | // By default, uses the default React Native `TextInput` component. 14 | TextInputComponent = TextInput, 15 | ...rest 16 | }, ref) { 17 | // Instead of `onChangeText(value: string)` it could use 18 | // `onChange(nativeEvent: Event)` and get `value` from `nativeEvent.text`. 19 | const onChangeText = useCallback((value) => { 20 | onChange({ 21 | preventDefault() { this.defaultPrevented = true }, 22 | target: { value } 23 | }) 24 | }, [onChange]) 25 | 26 | // React Native `` supports properties: 27 | // * `placeholder: string?` 28 | // * `autoFocus: boolean?` 29 | // * `value: string?` 30 | // plus the ones mentioned below: 31 | return ( 32 | 39 | ) 40 | } 41 | 42 | PhoneTextInput = React.forwardRef(PhoneTextInput) 43 | 44 | PhoneTextInput.propTypes = { 45 | /** 46 | * The input field `value: string`. 47 | */ 48 | value: PropTypes.string, 49 | 50 | /** 51 | * A function of `event: Event`. 52 | * Updates the `value: string` property. 53 | */ 54 | onChange: PropTypes.func.isRequired, 55 | 56 | /** 57 | * The standard `autoCompleteType` property of a React Native ``. 58 | */ 59 | autoCompleteType: PropTypes.string, 60 | 61 | /** 62 | * The input field component. 63 | */ 64 | TextInputComponent: PropTypes.elementType 65 | } 66 | 67 | export default PhoneTextInput 68 | -------------------------------------------------------------------------------- /source/helpers/parsePhoneNumberCharacter.js: -------------------------------------------------------------------------------- 1 | import { parsePhoneNumberCharacter } from 'libphonenumber-js/core' 2 | 3 | /** 4 | * Parses next character while parsing phone number digits (including a `+`) 5 | * from text: discards everything except `+` and digits, and `+` is only allowed 6 | * at the start of a phone number. 7 | * For example, is used in `react-phone-number-input` where it uses 8 | * [`input-format`](https://gitlab.com/catamphetamine/input-format). 9 | * @param {string} character - Yet another character from raw input string. 10 | * @param {string?} prevParsedCharacters - Previous parsed characters. 11 | * @param {object?} context - An optional object that could be used by this function to set arbitrary "flags". The object should be shared within the parsing of the whole string. 12 | * @return {string?} The parsed character. 13 | */ 14 | export default function parsePhoneNumberCharacter_(character, prevParsedCharacters, context) { 15 | // `context` argument was added as a third argument of `parse()` function 16 | // in `input-format` package on Dec 26th, 2023. So it could potentially be 17 | // `undefined` here if a 3rd-party app somehow ends up with this newer version 18 | // of `react-phone-number-input` and an older version of `input-format`. 19 | // Dunno how, but just in case, it could be `undefined` here and it wouldn't break. 20 | // Maybe it's not required to handle `undefined` case here. 21 | // 22 | // The addition of the `context` argument was to fix the slightly-weird behavior 23 | // of parsing an input string when the user inputs something like `"2+7" 24 | // https://github.com/catamphetamine/react-phone-number-input/issues/437 25 | // 26 | // If the parser encounters an unexpected `+` in a string being parsed 27 | // then it simply discards that out-of-place `+` and any following characters. 28 | // 29 | if (context && context.ignoreRest) { 30 | return 31 | } 32 | 33 | const emitEvent = (eventName) => { 34 | if (context) { 35 | switch (eventName) { 36 | case 'end': 37 | context.ignoreRest = true 38 | break 39 | } 40 | } 41 | } 42 | 43 | return parsePhoneNumberCharacter(character, prevParsedCharacters, emitEvent) 44 | } -------------------------------------------------------------------------------- /input/index.d.ts: -------------------------------------------------------------------------------- 1 | // React TypeScript Cheatsheet doesn't recommend using `React.FunctionalComponent`. 2 | // https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components 3 | 4 | import * as React from 'react'; 5 | 6 | import { 7 | Country, 8 | Value, 9 | ExternalValue, 10 | DefaultInputComponentProps 11 | } from '../index.d.js'; 12 | 13 | type InputComponent = 14 | | ((props: InputComponentProps) => JSX.Element | React.ComponentClass) 15 | | React.ForwardRefExoticComponent>; 16 | 17 | type FeaturePropsWithoutSmartCaret = Omit & { 18 | country?: Country; 19 | international?: boolean; 20 | withCountryCallingCode?: boolean; 21 | defaultCountry?: Country; 22 | inputComponent?: InputComponent; 23 | useNationalFormatForDefaultCountryValue?: boolean; 24 | } 25 | 26 | // `PropsWithoutSmartCaret` are imported in: 27 | // * `/react-native/index.d.ts`. 28 | export type PropsWithoutSmartCaret = FeaturePropsWithoutSmartCaret & { 29 | value?: Value | ExternalValue; 30 | onChange(value?: Value): void; 31 | } 32 | 33 | // `FeatureProps` are imported in: 34 | // * `/react-hook-form-input/index.d.ts`. 35 | export type FeatureProps = FeaturePropsWithoutSmartCaret & { 36 | smartCaret?: boolean; 37 | } 38 | 39 | // `Props` are imported in: 40 | // * `/input-core/index.d.ts` 41 | export type Props = PropsWithoutSmartCaret & { 42 | smartCaret?: boolean; 43 | } 44 | 45 | type PhoneInputComponentType = React.ForwardRefExoticComponent & React.RefAttributes> 46 | 47 | declare const PhoneInput: PhoneInputComponentType; 48 | 49 | export default PhoneInput; 50 | 51 | export { 52 | parsePhoneNumber, 53 | formatPhoneNumber, 54 | formatPhoneNumberIntl, 55 | isValidPhoneNumber, 56 | isPossiblePhoneNumber, 57 | getCountryCallingCode, 58 | getCountries, 59 | isSupportedCountry, 60 | Country, 61 | Value, 62 | PhoneNumber 63 | } from '../index.d.js'; 64 | -------------------------------------------------------------------------------- /website/docs/build/bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* 2 | object-assign 3 | (c) Sindre Sorhus 4 | @license MIT 5 | */ 6 | 7 | /*! 8 | Copyright (c) 2018 Jed Watson. 9 | Licensed under the MIT License (MIT), see 10 | http://jedwatson.github.io/classnames 11 | */ 12 | 13 | /*! 14 | * @overview es6-promise - a tiny implementation of Promises/A+. 15 | * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) 16 | * @license Licensed under MIT license 17 | * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE 18 | * @version v4.2.8+1e68dce6 19 | */ 20 | 21 | /*! 22 | * The buffer module from node.js, for the browser. 23 | * 24 | * @author Feross Aboukhadijeh 25 | * @license MIT 26 | */ 27 | 28 | /*! 29 | * regjsgen 0.5.2 30 | * Copyright 2014-2020 Benjamin Tan 31 | * Available under the MIT license 32 | */ 33 | 34 | /*! clipboard-copy. MIT License. Feross Aboukhadijeh */ 35 | 36 | /*! https://mths.be/regenerate v1.4.2 by @mathias | MIT license */ 37 | 38 | /** 39 | * @license React 40 | * react-dom.production.min.js 41 | * 42 | * Copyright (c) Facebook, Inc. and its affiliates. 43 | * 44 | * This source code is licensed under the MIT license found in the 45 | * LICENSE file in the root directory of this source tree. 46 | */ 47 | 48 | /** 49 | * @license React 50 | * react.production.min.js 51 | * 52 | * Copyright (c) Facebook, Inc. and its affiliates. 53 | * 54 | * This source code is licensed under the MIT license found in the 55 | * LICENSE file in the root directory of this source tree. 56 | */ 57 | 58 | /** 59 | * @license React 60 | * scheduler.production.min.js 61 | * 62 | * Copyright (c) Facebook, Inc. and its affiliates. 63 | * 64 | * This source code is licensed under the MIT license found in the 65 | * LICENSE file in the root directory of this source tree. 66 | */ 67 | 68 | /** 69 | * A better abstraction over CSS. 70 | * 71 | * @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present 72 | * @website https://github.com/cssinjs/jss 73 | * @license MIT 74 | */ 75 | 76 | /** 77 | * Prism: Lightweight, robust, elegant syntax highlighting 78 | * 79 | * @license MIT 80 | * @author Lea Verou 81 | * @namespace 82 | * @public 83 | */ 84 | -------------------------------------------------------------------------------- /react-hook-form/index.d.ts: -------------------------------------------------------------------------------- 1 | // React TypeScript Cheatsheet doesn't recommend using `React.FunctionalComponent` (`React.FC`). 2 | // https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components 3 | 4 | import * as React from 'react'; 5 | 6 | import { Control, FieldValues } from 'react-hook-form'; 7 | 8 | import { 9 | Value, 10 | ExternalValue, 11 | FeatureProps as BaseProps, 12 | DefaultInputComponentProps 13 | } from '../index.d.js'; 14 | 15 | export { 16 | Country, 17 | Value 18 | } from '../index.d.js'; 19 | 20 | // `ReactHookFormComponentProps` are used in: 21 | // * `react-hook-form-input/index.d.ts` 22 | export type ReactHookFormComponentProps = { 23 | name: string; 24 | defaultValue?: Value | ExternalValue; 25 | // A developer should pass a `control` object that is returned from `useForm()` hook. 26 | // Not required when using ``. 27 | control?: Control; 28 | rules?: object; 29 | // A quote from `react-hook-form`: 30 | // Without `shouldUnregister: true`, an input value would be retained when input is removed. 31 | // Setting `shouldUnregister: true` makes the form behave more closer to native. 32 | shouldUnregister?: boolean; 33 | } 34 | 35 | // `Props` are imported in: 36 | // * `react-hook-form-core/index.d.ts` 37 | export type Props = BaseProps & ReactHookFormComponentProps; 38 | 39 | // `DefaultFormValues` are imported in: 40 | // * `react-hook-form-core/index.d.ts` 41 | export type DefaultFormValues = FieldValues; 42 | 43 | type PhoneInputWithCountrySelectType = (props: Props) => JSX.Element; 44 | 45 | // Could also export the component that would accept custom "generics", if the component was a function, 46 | // but seems like it would also introduce some inconvenience when using `typeof PhoneInputWithCountrySelect` 47 | // for defining the type of the `props`. 48 | // https://github.com/catamphetamine/react-phone-number-input/issues/414#issuecomment-1220679025 49 | // type PhoneInputWithCountrySelectType = (props: Props) => JSX.Element; 50 | 51 | declare const PhoneInputWithCountrySelect: PhoneInputWithCountrySelectType; 52 | 53 | export default PhoneInputWithCountrySelect; 54 | -------------------------------------------------------------------------------- /source/helpers/countries.js: -------------------------------------------------------------------------------- 1 | // Ignores weird istanbul error: "else path not taken". 2 | import { isSupportedCountry } from 'libphonenumber-js/core' 3 | export { getCountries } from 'libphonenumber-js/core' 4 | 5 | /** 6 | * Sorts country `` options 8 | * to the top of the list, for example. 9 | * @param {object[]} countryOptions — Country `` options order. Example: `["US", "CA", "AU", "|", "..."]`. 11 | * @return {object[]} 12 | */ 13 | export function sortCountryOptions(options, order) { 14 | if (!order) { 15 | return options 16 | } 17 | const optionsOnTop = [] 18 | const optionsOnBottom = [] 19 | let appendTo = optionsOnTop 20 | for (const element of order) { 21 | if (element === '|') { 22 | appendTo.push({ divider: true }) 23 | } else if (element === '...' || element === '…') { 24 | appendTo = optionsOnBottom 25 | } else { 26 | let countryCode 27 | if (element === '🌐') { 28 | countryCode = undefined 29 | } else { 30 | countryCode = element 31 | } 32 | // Find the position of the option. 33 | const index = options.indexOf(options.filter(option => option.value === countryCode)[0]) 34 | // Get the option. 35 | const option = options[index] 36 | // Remove the option from its default position. 37 | options.splice(index, 1) 38 | // Add the option on top. 39 | appendTo.push(option) 40 | } 41 | } 42 | return optionsOnTop.concat(options).concat(optionsOnBottom) 43 | } 44 | 45 | export function getSupportedCountryOptions(countryOptions, metadata) { 46 | if (countryOptions) { 47 | countryOptions = countryOptions.filter((option) => { 48 | switch (option) { 49 | case '🌐': 50 | case '|': 51 | case '...': 52 | case '…': 53 | return true 54 | default: 55 | return isCountrySupportedWithError(option, metadata) 56 | } 57 | }) 58 | if (countryOptions.length > 0) { 59 | return countryOptions 60 | } 61 | } 62 | } 63 | 64 | export function isCountrySupportedWithError(country, metadata) { 65 | if (isSupportedCountry(country, metadata)) { 66 | return true 67 | } else { 68 | console.error(`Country not found: ${country}`) 69 | return false 70 | } 71 | } 72 | 73 | export function getSupportedCountries(countries, metadata) { 74 | if (countries) { 75 | countries = countries.filter(country => isCountrySupportedWithError(country, metadata)) 76 | if (countries.length === 0) { 77 | countries = undefined 78 | } 79 | } 80 | return countries 81 | } -------------------------------------------------------------------------------- /source/useExternalRef.js: -------------------------------------------------------------------------------- 1 | import { useRef, useCallback } from 'react' 2 | 3 | /** 4 | * This hook creates an internal copy of a `ref` 5 | * and returns a new `ref`-alike setter function 6 | * that updates both `ref` and the internal copy of it. 7 | * That `ref`-alike setter function could then be passed 8 | * to child elements instead of the original `ref`. 9 | * 10 | * The internal copy of the `ref` can then be used to 11 | * call instance methods like `.focus()`, etc. 12 | * 13 | * One may ask: why create a copy of `ref` for "internal" use 14 | * when the code could use the original `ref` for that. 15 | * The answer is: the code would have to dance around the original `ref` anyway 16 | * to figure out whether it exists and to find out the internal implementation of it 17 | * in order to read its value correctly. This hook encapsulates all that "boilerplate" code. 18 | * The returned copy of the `ref` is guaranteed to exist and functions as a proper ref "object". 19 | * The returned `ref`-alike setter function must be used instead of the original `ref` 20 | * when passing it to child elements. 21 | * 22 | * @param {(object|function)} [externalRef] — The original `ref` that may have any internal implementation and might not even exist. 23 | * @return {any[]} Returns an array of two elements: a copy of the `ref` for "internal" use and a `ref`-alike setter function that should be used in-place of the original `ref` when passing it to child elements. 24 | */ 25 | export default function useExternalRef(externalRef) { 26 | // Create a copy of the original `ref` (which might not exist). 27 | // Both refs will point to the same value. 28 | const refCopy = useRef() 29 | 30 | // Updates both `ref`s with the same `value`. 31 | const refSetter = useCallback((value) => { 32 | setRefsValue([externalRef, refCopy], value) 33 | }, [ 34 | externalRef, 35 | refCopy 36 | ]) 37 | 38 | return [refCopy, refSetter] 39 | } 40 | 41 | // Sets the same `value` of all `ref`s. 42 | // Some of the `ref`s may not exist in which case they'll be skipped. 43 | export function setRefsValue(refs, value) { 44 | for (const ref of refs) { 45 | if (ref) { 46 | setRefValue(ref, value) 47 | } 48 | } 49 | } 50 | 51 | // Sets the value of a `ref`. 52 | // Before React Hooks were introduced, `ref`s used to be functions. 53 | // After React Hooks were introduces, `ref`s became objects with `.current` property. 54 | // This function sets a `ref`'s value regardless of its internal implementation, 55 | // so it supports both types of `ref`s. 56 | function setRefValue(ref, value) { 57 | if (typeof ref === 'function') { 58 | ref(value) 59 | } else { 60 | ref.current = value 61 | } 62 | } -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import json from 'rollup-plugin-json' 2 | import commonjs from 'rollup-plugin-commonjs' 3 | import resolve from 'rollup-plugin-node-resolve' 4 | import { terser } from 'rollup-plugin-terser' 5 | 6 | const resolveModules = resolve() 7 | 8 | const COMMON_PLUGINS = [ 9 | resolveModules, 10 | commonjs(), 11 | json(), 12 | terser() 13 | ]; 14 | 15 | const COMMON_OUTPUT = { 16 | format: 'umd', 17 | name: 'PhoneInput', 18 | sourcemap: true, 19 | exports: 'named', 20 | globals: { 21 | 'react': 'React', 22 | 'prop-types': 'PropTypes', 23 | 'react-hook-form': 'ReactHookForm' 24 | } 25 | }; 26 | 27 | const COMMON_EXTERNAL = ['react', 'prop-types'] 28 | 29 | export default [ 30 | { 31 | input: 'min/index.js', 32 | plugins: COMMON_PLUGINS, 33 | external: COMMON_EXTERNAL, 34 | output: { 35 | file: 'bundle/react-phone-number-input.js', 36 | ...COMMON_OUTPUT 37 | } 38 | }, 39 | { 40 | input: 'mobile/index.js', 41 | plugins: COMMON_PLUGINS, 42 | external: COMMON_EXTERNAL, 43 | output: { 44 | file: 'bundle/react-phone-number-input-mobile.js', 45 | ...COMMON_OUTPUT 46 | } 47 | }, 48 | { 49 | input: 'max/index.js', 50 | plugins: COMMON_PLUGINS, 51 | external: COMMON_EXTERNAL, 52 | output: { 53 | file: 'bundle/react-phone-number-input-max.js', 54 | ...COMMON_OUTPUT 55 | } 56 | }, 57 | { 58 | input: 'input/index.js', 59 | plugins: COMMON_PLUGINS, 60 | external: COMMON_EXTERNAL, 61 | output: { 62 | file: 'bundle/react-phone-number-input-input.js', 63 | ...COMMON_OUTPUT 64 | } 65 | }, 66 | { 67 | input: 'input-mobile/index.js', 68 | plugins: COMMON_PLUGINS, 69 | external: COMMON_EXTERNAL, 70 | output: { 71 | file: 'bundle/react-phone-number-input-input-mobile.js', 72 | ...COMMON_OUTPUT 73 | } 74 | }, 75 | { 76 | input: 'input-max/index.js', 77 | plugins: COMMON_PLUGINS, 78 | external: COMMON_EXTERNAL, 79 | output: { 80 | file: 'bundle/react-phone-number-input-input-max.js', 81 | ...COMMON_OUTPUT 82 | } 83 | }, 84 | { 85 | input: 'react-hook-form/index.js', 86 | plugins: COMMON_PLUGINS, 87 | external: COMMON_EXTERNAL, 88 | output: { 89 | file: 'bundle/react-phone-number-input-react-hook-form.js', 90 | ...COMMON_OUTPUT 91 | } 92 | }, 93 | { 94 | input: 'react-hook-form-input/index.js', 95 | plugins: COMMON_PLUGINS, 96 | external: COMMON_EXTERNAL, 97 | output: { 98 | file: 'bundle/react-phone-number-input-react-hook-form-input.js', 99 | ...COMMON_OUTPUT 100 | } 101 | } 102 | ] 103 | -------------------------------------------------------------------------------- /scripts/generate-locale-exports.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | 4 | // Stupid Node.js can't even `import` JSON files. 5 | // https://stackoverflow.com/questions/72348042/typeerror-err-unknown-file-extension-unknown-file-extension-json-for-node 6 | // Using a `*.json.js` duplicate file workaround. 7 | createLocaleJsonJsFiles(getAllLocales()) 8 | 9 | createLocaleJsonTypeScriptDefinitionFiles(getAllLocales()) 10 | 11 | addLocaleExports(getAllLocales()) 12 | 13 | /** 14 | * Returns a list of all locales supported by `relative-time-format`. 15 | * @return {string[]} 16 | */ 17 | function getAllLocales() { 18 | const LOCALE_FILE_NAME_REG_EXP = /([^\/]+)\.json$/ 19 | return fs.readdirSync(path.join('./locale/')) 20 | .filter(_ => fs.statSync(path.join('./locale', _)).isFile() && LOCALE_FILE_NAME_REG_EXP.test(_)) 21 | .map(_ => _.match(LOCALE_FILE_NAME_REG_EXP)[1]) 22 | } 23 | 24 | // Add `export` entries in `package.json`. 25 | function addLocaleExports(ALL_LOCALES) { 26 | // Read `package.json` file. 27 | const packageJson = readJsonFromFile('./package.json') 28 | 29 | // Remove all locale exports. 30 | for (const path of Object.keys(packageJson.exports)) { 31 | if (path.startsWith('./locale/')) { 32 | delete packageJson.exports[path] 33 | } 34 | } 35 | 36 | // Re-add all locale exports. 37 | packageJson.exports = { 38 | ...packageJson.exports, 39 | ...ALL_LOCALES.reduce((all, locale) => { 40 | all[`./locale/${locale}`] = { 41 | types: `./locale/${locale}.json.d.ts`, 42 | import: `./locale/${locale}.json.js`, 43 | require: `./locale/${locale}.json` 44 | } 45 | all[`./locale/${locale}.json`] = { 46 | types: `./locale/${locale}.json.d.ts`, 47 | import: `./locale/${locale}.json.js`, 48 | require: `./locale/${locale}.json` 49 | } 50 | return all 51 | }, {}) 52 | } 53 | 54 | // Save `package.json` file. 55 | fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2) + '\n', 'utf8') 56 | } 57 | 58 | function readJsonFromFile(path) { 59 | return JSON.parse(fs.readFileSync(path, 'utf8')) 60 | } 61 | 62 | // Stupid Node.js can't even `import` JSON files. 63 | // https://stackoverflow.com/questions/72348042/typeerror-err-unknown-file-extension-unknown-file-extension-json-for-node 64 | // Using a `*.json.js` duplicate file workaround. 65 | function createLocaleJsonJsFiles(locales) { 66 | for (const locale of locales) { 67 | const localeData = readJsonFromFile(`./locale/${locale}.json`) 68 | fs.writeFileSync(`./locale/${locale}.json.js`, 'export default ' + JSON.stringify(localeData, null, 2), 'utf8') 69 | } 70 | } 71 | 72 | function createLocaleJsonTypeScriptDefinitionFiles(locales) { 73 | for (const locale of locales) { 74 | fs.writeFileSync( 75 | `./locale/${locale}.json.d.ts`, 76 | ` 77 | import { LabelKey } from '../index' 78 | type Locale = { [key in LabelKey]: string } 79 | declare const Locale: Locale 80 | export default Locale 81 | `.trim() 82 | ) 83 | } 84 | } -------------------------------------------------------------------------------- /source/InternationalIcon.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | 4 | export default function InternationalIcon({ aspectRatio, ...rest }) { 5 | if (aspectRatio === 1) { 6 | return 7 | } else { 8 | return 9 | } 10 | } 11 | 12 | InternationalIcon.propTypes = { 13 | title: PropTypes.string.isRequired, 14 | aspectRatio: PropTypes.number 15 | } 16 | 17 | // 3x2. 18 | // Using `` in `<svg/>`s: 19 | // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title 20 | function InternationalIcon3x2({ title, ...rest }) { 21 | return ( 22 | <svg 23 | {...rest} 24 | xmlns="http://www.w3.org/2000/svg" 25 | viewBox="0 0 75 50"> 26 | <title>{title} 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | ) 47 | } 48 | 49 | InternationalIcon3x2.propTypes = { 50 | title: PropTypes.string.isRequired 51 | } 52 | 53 | // 1x1. 54 | // Using `` in `<svg/>`s: 55 | // https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title 56 | function InternationalIcon1x1({ title, ...rest }) { 57 | return ( 58 | <svg 59 | {...rest} 60 | xmlns="http://www.w3.org/2000/svg" 61 | viewBox="0 0 50 50"> 62 | <title>{title} 63 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 81 | 82 | ) 83 | } 84 | 85 | InternationalIcon1x1.propTypes = { 86 | title: PropTypes.string.isRequired 87 | } 88 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and free environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a censorship-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating an open and free environment 15 | include: 16 | 17 | * Not constraining the language to be "welcoming" or "inclusive" 18 | * Not demanding show of empathy towards other community members 19 | * Not dictating anyone to be respectful of differing viewpoints and experiences 20 | * Not forcing anyone to change their views or opinions regardless of those 21 | * Not intimidating other people into accepting your own views or opinions 22 | * Not blackmailing other people to disclose their personal views or opinions 23 | * Not constraining other people from publishing their personal views or opinions in an unintrusive way 24 | * Focusing on what is best for the ecosystem 25 | 26 | Examples of acceptable behavior by participants include: 27 | 28 | * The use of sexualized language 29 | * Occasional trolling or insulting comments that are not completely off-topic 30 | 31 | Examples of unacceptable behavior by participants include: 32 | 33 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 34 | * Unwelcome sexual attention or advances 35 | * Public harassment or personal attacks when carried out in an bold or intrusive way 36 | * Private harassment 37 | * Any actions that are in violation of the local laws or otherwise considered illegal 38 | * Other conduct which could reasonably be considered inappropriate in an open and free setting 39 | 40 | ## Our Responsibilities 41 | 42 | Project maintainers are responsible for clarifying the standards of acceptable 43 | behavior and are free to take appropriate and fair corrective action in 44 | response to any instances of unacceptable behavior. 45 | 46 | Project maintainers have the right and authority to remove, edit, or 47 | reject comments, commits, code, wiki edits, issues, and other contributions 48 | that are not aligned to this Code of Conduct, or to ban temporarily or 49 | permanently any contributor for other behaviors that they deem inappropriate, 50 | threatening, offensive, or harmful. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies both within project spaces and in public spaces 55 | when an individual is representing the project or its community. Examples of 56 | representing a project or community include using an official project e-mail 57 | address, posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. Representation of a project may be 59 | further defined and clarified by project maintainers. 60 | 61 | ## Enforcement 62 | 63 | Instances of unacceptable behavior may be reported by contacting the project team. 64 | The complaints will likely be reviewed and investigated and may result in a response that 65 | is deemed necessary and appropriate to the circumstances. The project team should maintain confidentiality with regard to the reporter of an incident. 66 | Further details of specific enforcement policies may be posted separately. 67 | 68 | Project maintainers who do not follow the Code of Conduct in good 69 | faith may face temporary or permanent repercussions as determined by other 70 | members of the project's leadership. 71 | 72 | ## Attribution 73 | 74 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 75 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 76 | 77 | [homepage]: https://www.contributor-covenant.org 78 | 79 | -------------------------------------------------------------------------------- /source/helpers/countries.test.js: -------------------------------------------------------------------------------- 1 | import metadata from 'libphonenumber-js/min/metadata' 2 | 3 | import { 4 | sortCountryOptions, 5 | getSupportedCountryOptions, 6 | isCountrySupportedWithError, 7 | getSupportedCountries 8 | } from './countries.js' 9 | 10 | describe('sortCountryOptions', () => { 11 | it('should sort country options (no `order`)', () => { 12 | sortCountryOptions([ 13 | { 14 | value: 'RU', 15 | label: 'Russia' 16 | }, 17 | { 18 | value: 'US', 19 | label: 'United States' 20 | } 21 | ]).should.deep.equal([ 22 | { 23 | value: 'RU', 24 | label: 'Russia' 25 | }, 26 | { 27 | value: 'US', 28 | label: 'United States' 29 | } 30 | ]) 31 | }) 32 | 33 | it('should sort country options (with a divider)', () => { 34 | sortCountryOptions( 35 | [ 36 | { 37 | value: 'RU', 38 | label: 'Russia' 39 | }, 40 | { 41 | value: 'US', 42 | label: 'United States' 43 | } 44 | ], 45 | ['US', '|', 'RU'] 46 | ).should.deep.equal([ 47 | { 48 | value: 'US', 49 | label: 'United States' 50 | }, 51 | { 52 | divider: true 53 | }, 54 | { 55 | value: 'RU', 56 | label: 'Russia' 57 | } 58 | ]) 59 | }) 60 | 61 | it('should sort country options (with "...")', () => { 62 | sortCountryOptions( 63 | [ 64 | { 65 | value: 'RU', 66 | label: 'Russia' 67 | }, 68 | { 69 | value: 'US', 70 | label: 'United States' 71 | } 72 | ], 73 | ['US', '|', '...'] 74 | ).should.deep.equal([ 75 | { 76 | value: 'US', 77 | label: 'United States' 78 | }, 79 | { 80 | divider: true 81 | }, 82 | { 83 | value: 'RU', 84 | label: 'Russia' 85 | } 86 | ]) 87 | }) 88 | 89 | it('should sort country options (with "…")', () => { 90 | sortCountryOptions( 91 | [ 92 | { 93 | value: 'RU', 94 | label: 'Russia' 95 | }, 96 | { 97 | value: 'US', 98 | label: 'United States' 99 | } 100 | ], 101 | ['US', '|', '…'] 102 | ).should.deep.equal([ 103 | { 104 | value: 'US', 105 | label: 'United States' 106 | }, 107 | { 108 | divider: true 109 | }, 110 | { 111 | value: 'RU', 112 | label: 'Russia' 113 | } 114 | ]) 115 | }) 116 | 117 | it('should sort country options (with "🌐")', () => { 118 | sortCountryOptions( 119 | [ 120 | { 121 | value: 'RU', 122 | label: 'Russia' 123 | }, 124 | { 125 | label: 'International' 126 | }, 127 | { 128 | value: 'US', 129 | label: 'United States' 130 | } 131 | ], 132 | ['US', '🌐', '…'] 133 | ).should.deep.equal([ 134 | { 135 | value: 'US', 136 | label: 'United States' 137 | }, 138 | { 139 | label: 'International' 140 | }, 141 | { 142 | value: 'RU', 143 | label: 'Russia' 144 | } 145 | ]) 146 | }) 147 | }) 148 | 149 | describe('getSupportedCountryOptions', () => { 150 | it('should get supported country options', () => { 151 | getSupportedCountryOptions([ 152 | '🌐', 153 | 'RU', 154 | 'XX', 155 | '@', 156 | '|', 157 | '…', 158 | '...', 159 | '.' 160 | ], metadata).should.deep.equal([ 161 | '🌐', 162 | 'RU', 163 | '|', 164 | '…', 165 | '...' 166 | ]) 167 | }) 168 | 169 | it('should get supported country options (none supported)', () => { 170 | expect(getSupportedCountryOptions([ 171 | 'XX', 172 | '@', 173 | '.' 174 | ], metadata)).to.be.undefined 175 | }) 176 | 177 | it('should get supported country options (none supplied)', () => { 178 | expect(getSupportedCountryOptions(undefined, metadata)).to.be.undefined 179 | }) 180 | 181 | it('should tell is country is supported with error', () => { 182 | isCountrySupportedWithError('RU', metadata).should.equal(true) 183 | isCountrySupportedWithError('XX', metadata).should.equal(false) 184 | }) 185 | 186 | it('should get supported countries', () => { 187 | getSupportedCountries(['RU', 'XX'], metadata).should.deep.equal(['RU']) 188 | }) 189 | 190 | it('should get supported countries (none supported)', () => { 191 | expect(getSupportedCountries(['XX'], metadata)).to.be.undefined 192 | }) 193 | }) -------------------------------------------------------------------------------- /source/InputSmart.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react' 2 | import PropTypes from 'prop-types' 3 | import Input from 'input-format/react' 4 | import { AsYouType } from 'libphonenumber-js/core' 5 | 6 | import { getPrefixForFormattingValueAsPhoneNumber, removePrefixFromFormattedPhoneNumber } from './helpers/inputValuePrefix.js' 7 | import parsePhoneNumberCharacter from './helpers/parsePhoneNumberCharacter.js' 8 | 9 | import useInputKeyDownHandler from './useInputKeyDownHandler.js' 10 | 11 | export function createInput(defaultMetadata) 12 | { 13 | /** 14 | * `InputSmart` is a "smarter" implementation of a `Component` 15 | * that can be passed to ``. It parses and formats 16 | * the user's and maintains the caret's position in the process. 17 | * The caret positioning is maintained using `input-format` library. 18 | * Relies on being run in a DOM environment for calling caret positioning functions. 19 | */ 20 | function InputSmart({ 21 | onKeyDown, 22 | country, 23 | inputFormat, 24 | metadata = defaultMetadata, 25 | // 26 | // The rest of the properties listed here are just to get the `rest` props 27 | // that will be passed to the DOM `` element. 28 | // 29 | // `international` property is deprecated and is not used. 30 | international, 31 | // `withCountryCallingCode` property is deprecated and is not used. 32 | withCountryCallingCode, 33 | ...rest 34 | }, ref) { 35 | const format = useCallback((value) => { 36 | // "As you type" formatter. 37 | const formatter = new AsYouType(country, metadata) 38 | 39 | const prefix = getPrefixForFormattingValueAsPhoneNumber({ 40 | inputFormat, 41 | country, 42 | metadata 43 | }) 44 | 45 | // Format the number. 46 | let text = formatter.input(prefix + value) 47 | let template = formatter.getTemplate() 48 | 49 | if (prefix) { 50 | text = removePrefixFromFormattedPhoneNumber(text, prefix) 51 | // `AsYouType.getTemplate()` can be `undefined`. 52 | if (template) { 53 | template = removePrefixFromFormattedPhoneNumber(template, prefix) 54 | } 55 | } 56 | 57 | return { 58 | text, 59 | template 60 | } 61 | }, [ 62 | country, 63 | metadata 64 | ]) 65 | 66 | const _onKeyDown = useInputKeyDownHandler({ 67 | onKeyDown, 68 | inputFormat 69 | }) 70 | 71 | return ( 72 | 79 | ) 80 | } 81 | 82 | InputSmart = React.forwardRef(InputSmart) 83 | 84 | InputSmart.propTypes = { 85 | /** 86 | * The parsed phone number. 87 | * "Parsed" not in a sense of "E.164" 88 | * but rather in a sense of "having only 89 | * digits and possibly a leading plus character". 90 | * Examples: `""`, `"+"`, `"+123"`, `"123"`. 91 | */ 92 | value: PropTypes.string.isRequired, 93 | 94 | /** 95 | * A function of `value: string`. 96 | * Updates the `value` property. 97 | */ 98 | onChange: PropTypes.func.isRequired, 99 | 100 | /** 101 | * A function of `event: Event`. 102 | * Handles `keydown` events. 103 | */ 104 | onKeyDown: PropTypes.func, 105 | 106 | /** 107 | * A two-letter country code for formatting `value` 108 | * as a national phone number (e.g. `(800) 555 35 35`). 109 | * E.g. "US", "RU", etc. 110 | * If no `country` is passed then `value` 111 | * is formatted as an international phone number. 112 | * (e.g. `+7 800 555 35 35`) 113 | * This property should've been called `defaultCountry` 114 | * because it only applies when the user inputs a phone number in a national format 115 | * and is completely ignored when the user inputs a phone number in an international format. 116 | */ 117 | country: PropTypes.string, 118 | 119 | /** 120 | * The format that the input field value is being input/output in. 121 | */ 122 | inputFormat : PropTypes.oneOf([ 123 | 'INTERNATIONAL', 124 | 'NATIONAL_PART_OF_INTERNATIONAL', 125 | 'NATIONAL', 126 | 'INTERNATIONAL_OR_NATIONAL' 127 | ]).isRequired, 128 | 129 | /** 130 | * `libphonenumber-js` metadata. 131 | */ 132 | metadata: PropTypes.object 133 | } 134 | 135 | return InputSmart 136 | } 137 | 138 | export default createInput() -------------------------------------------------------------------------------- /source/CountrySelect.js: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useMemo } from 'react' 2 | import PropTypes from 'prop-types' 3 | import classNames from 'classnames' 4 | import getUnicodeFlagIcon from 'country-flag-icons/unicode' 5 | 6 | export default function CountrySelect({ 7 | value, 8 | onChange, 9 | options, 10 | disabled, 11 | readOnly, 12 | ...rest 13 | }) { 14 | const onChange_ = useCallback((event) => { 15 | const value = event.target.value 16 | onChange(value === 'ZZ' ? undefined : value) 17 | }, [onChange]) 18 | 19 | const selectedOption = useMemo(() => { 20 | return getSelectedOption(options, value) 21 | }, [options, value]) 22 | 23 | // "ZZ" means "International". 24 | // (HTML requires each `