├── .github
└── workflows
│ ├── playground.yml
│ ├── publish.yml
│ └── tests.yml
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── babel.config.js
├── examples
├── README.md
├── antd4.x
│ ├── craco.config.ts
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── Demo.tsx
│ │ ├── index.tsx
│ │ └── themes
│ │ │ ├── Dark.tsx
│ │ │ └── Light.tsx
│ └── tsconfig.json
├── antd5.x-next
│ ├── .gitignore
│ ├── next-env.d.ts
│ ├── next.config.mjs
│ ├── package.json
│ ├── src
│ │ └── app
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ └── tsconfig.json
├── antd5.x
│ ├── package.json
│ ├── public
│ │ └── index.html
│ ├── src
│ │ ├── Demo.tsx
│ │ └── index.tsx
│ └── tsconfig.json
└── deploy.sh
├── jestconfig.json
├── package.json
├── scripts
├── prepare-package.ts
└── tsconfig.json
├── src
├── index.tsx
├── locale.ts
├── resources
│ └── stylesheet.json
├── styles.ts
└── types.ts
├── tests
└── antd.test.tsx
├── tox.ini
└── tsconfig.json
/.github/workflows/playground.yml:
--------------------------------------------------------------------------------
1 | name: playground
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 |
7 | jobs:
8 | deploy:
9 | environment:
10 | name: Playground
11 | url: https://playground.typesnippet.org/antd-phone-input-5.x
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Run deployment script on server
15 | uses: appleboy/ssh-action@master
16 | with:
17 | host: ${{ secrets.HOST }}
18 | username: ${{ secrets.USERNAME }}
19 | key: ${{ secrets.KEY_ED25519 }}
20 | port: ${{ secrets.PORT }}
21 | script: bash ~/antd-phone-input/examples/deploy.sh
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to NPM
2 |
3 | on:
4 | release:
5 | types: [ published ]
6 |
7 | jobs:
8 | deploy:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v3
15 |
16 | - name: Set up Node
17 | uses: actions/setup-node@v3
18 | with:
19 | node-version: 20.x
20 | registry-url: https://registry.npmjs.org/
21 |
22 | - name: Install dependencies
23 | run: yarn && yarn install
24 |
25 | - name: Build and publish
26 | env:
27 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
28 | run: yarn build && yarn publish
29 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: tests
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | tests:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | strategy:
15 | matrix:
16 | node-version: [ 16.x, 18.x, 20.x ]
17 | antd-version: [ 424, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 5100, 5110 ]
18 |
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v3
22 |
23 | - name: Set up Python
24 | uses: actions/setup-python@v2
25 |
26 | - name: Install Tox
27 | run: |
28 | pip install --upgrade pip
29 | pip install tox tox-gh-actions
30 |
31 | - name: Set up Node
32 | uses: actions/setup-node@v3
33 | with:
34 | node-version: ${{ matrix.node-version }}
35 | registry-url: https://registry.npmjs.org/
36 |
37 | - name: Install dependencies
38 | run: npm install
39 |
40 | - name: Run Tox Jest
41 | run: tox -e antd_v${{ matrix.antd-version }}_jest
42 |
43 | build:
44 |
45 | runs-on: ubuntu-latest
46 |
47 | strategy:
48 | matrix:
49 | antd-version: [ 424, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 5100, 5110 ]
50 |
51 | steps:
52 | - name: Checkout
53 | uses: actions/checkout@v3
54 |
55 | - name: Set up Python
56 | uses: actions/setup-python@v2
57 |
58 | - name: Install Tox
59 | run: |
60 | pip install --upgrade pip
61 | pip install tox tox-gh-actions
62 |
63 | - name: Set up Node
64 | uses: actions/setup-node@v3
65 | with:
66 | node-version: 16.x
67 | registry-url: https://registry.npmjs.org/
68 |
69 | - name: Build and Package
70 | run: |
71 | npm install
72 | npm run build
73 | npm pack
74 |
75 | - name: Run Tox
76 | run: tox -e antd_v${{ matrix.antd-version }}
77 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Node artifact files
2 | node_modules/
3 | package-lock.json
4 |
5 | # JetBrains IDE
6 | .idea/
7 |
8 | # Build files
9 | ./metadata/
10 | ./styles*
11 | ./types*
12 | ./index*
13 |
14 | # Pycache
15 | __pycache__/
16 | *.py[cod]
17 | *$py.class
18 |
19 | # Tarballs
20 | *.tgz
21 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | examples
2 | scripts
3 | tests
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Artyom Vancyan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Antd Phone Input
2 |
3 | [](https://www.npmjs.com/package/antd-phone-input)
4 | [](https://playground.typesnippet.org/antd-phone-input-5.x)
5 | [](https://github.com/ant-design/ant-design)
6 | [](https://www.npmjs.com/package/antd-phone-input)
7 | [](https://github.com/typesnippet/antd-phone-input/blob/master/LICENSE)
8 | [](https://github.com/typesnippet/antd-phone-input/actions/workflows/tests.yml)
9 |
10 |
Advanced phone input component for Material UI that leverages the react-phone-hooks supporting all countries. The package is compatible with antd 4 and 5 versions. It provides built-in support for area codes and strict validation.
17 |
18 | ## Value
19 |
20 | The value of the component is an object containing the parts of the phone number. This format of value gives a wide range of opportunities for handling the data in your desired way.
21 |
22 | ```javascript
23 | {
24 | countryCode: 1,
25 | areaCode: "702",
26 | phoneNumber: "1234567",
27 | isoCode: "us",
28 | valid: function valid(strict)
29 | }
30 | ```
31 |
32 | ## Validation
33 |
34 | The validation is checked by the `valid` function of the value object that returns a boolean value. An example with the [react-hook-form](https://www.npmjs.com/package/react-hook-form) is shown [here](https://github.com/typesnippet/antd-phone-input/issues/64#issuecomment-1855867861).
35 |
36 | ```javascript
37 | import React from "react";
38 | import PhoneInput from "antd-phone-input";
39 | import FormItem from "antd/es/form/FormItem";
40 |
41 | const validator = (_, {valid}) => {
42 | // if (valid(true)) return Promise.resolve(); // strict validation
43 | if (valid()) return Promise.resolve(); // non-strict validation
44 | return Promise.reject("Invalid phone number");
45 | }
46 |
47 | const Demo = () => {
48 | return (
49 |
50 |
51 |
52 | )
53 | }
54 |
55 | export default Demo;
56 | ```
57 |
58 | The `valid` function primarily checks if a phone number has a length appropriate for its specified country. In addition, a more comprehensive validation can be performed, including verifying the dial and area codes' accuracy for the selected country. To activate the strict validation, pass `true` as the first argument to the `valid` function.
59 |
60 | ## Localization
61 |
62 | The package provides a built-in localization feature that allows you to change the language of the component. The `locale` function returns the language object that can be passed to the `ConfigProvider` component of Ant Design.
63 |
64 | ```javascript
65 | import PhoneInput, {locale} from "antd-phone-input";
66 |
67 |
68 |
69 |
70 | ```
71 |
72 | NOTE: If you use localization in the [documented](https://ant.design/docs/react/i18n) way, you should replace the object passed to the `locale` property with the `locale` function, specifying the desired language code.
73 |
74 | ## Props
75 |
76 | Apart from the phone-specific properties described below, all [Input](https://ant.design/components/input#input) properties supported by the used Ant Design version can be applied to the phone input component.
77 |
78 | | Property | Description | Type |
79 | |--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
80 | | value | An object containing a parsed phone number or the raw number. This also applies to the `initialValue` property of [Form.Item](https://ant.design/components/form#formitem). | [object](#value) / string |
81 | | country | Country code to be selected by default. By default, it will show the flag of the user's country. | string |
82 | | distinct | Show the distinct list of country codes not listing all area codes. | boolean |
83 | | enableArrow | Shows an arrow next to the country flag. Default value is `false`. | boolean |
84 | | enableSearch | Enables search in the country selection dropdown menu. Default value is `false`. | boolean |
85 | | searchNotFound | The value is shown if `enableSearch` is `true` and the query does not match any country. Default value is `No country found`. | string |
86 | | searchPlaceholder | The value is shown if `enableSearch` is `true`. Default value is `Search country`. | string |
87 | | disableDropdown | Disables the manual country selection through the dropdown menu. | boolean |
88 | | disableParentheses | Disables parentheses from the input masks. Default enabled. | boolean |
89 | | onlyCountries | Country codes to be included in the list. E.g. `onlyCountries={['us', 'ca', 'uk']}`. | string[] |
90 | | excludeCountries | Country codes to be excluded from the list of countries. E.g. `excludeCountries={['us', 'ca', 'uk']}`. | string[] |
91 | | preferredCountries | Country codes to be at the top of the list. E.g. `preferredCountries={['us', 'ca', 'uk']}`. | string[] |
92 | | dropdownRender | Customize the dropdown menu content. | (menu: ReactNode) => ReactNode |
93 | | onChange | The only difference from the original `onChange` is that value comes first. | function(value, event) |
94 | | onMount | The callback is triggered once the component gets mounted. | function(value) |
95 |
96 | ## Contribute
97 |
98 | Any contribution is welcome. Don't hesitate to open an issue or discussion if you have questions about your project's usage and integration. For ideas or suggestions, please open a pull request. Your name will shine on our contributors' list. Be proud of what you build!
99 |
100 | ## License
101 |
102 | Copyright (C) 2023 Artyom Vancyan. [MIT](LICENSE)
103 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | "@babel/preset-env",
5 | {
6 | targets: {
7 | node: "current",
8 | },
9 | },
10 | ],
11 | ],
12 | };
13 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | antd4.x and antd5.x examples allow you to play with the phone input with different numbers and preview them. They also
4 | allow you to switch between light and dark themes and test them out.
5 |
6 | ## Running the examples
7 |
8 | For running the antd4.x example, please use node v14 or below (npm <= v6).
9 |
10 | ```bash
11 | cd examples/antd4.x
12 | nvm use 14
13 | npm install
14 | npm start
15 | ```
16 |
17 | And for running the antd5.x example, please use node v14 or above (npm >= v6).
18 |
19 | ```bash
20 | cd examples/antd5.x
21 | nvm use 16 # this is optional
22 | npm install
23 | npm start
24 | ```
25 |
--------------------------------------------------------------------------------
/examples/antd4.x/craco.config.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: [
3 | {
4 | plugin: require("craco-less"),
5 | options: {
6 | lessLoaderOptions: {
7 | lessOptions: {
8 | javascriptEnabled: true,
9 | },
10 | },
11 | },
12 | },
13 | ],
14 | };
15 |
--------------------------------------------------------------------------------
/examples/antd4.x/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sample",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@craco/craco": "^7.0.0",
7 | "@types/react": "^18.2.0",
8 | "@types/react-dom": "^18.2.0",
9 | "antd": "^4.24.8",
10 | "antd-phone-input": "^0.3.13",
11 | "craco-less": "^2.0.0",
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0",
14 | "react-scripts": "^5.0.1",
15 | "typescript": "^4.9.5"
16 | },
17 | "scripts": {
18 | "start": "craco start",
19 | "build": "craco build"
20 | },
21 | "eslintConfig": {
22 | "extends": [
23 | "react-app"
24 | ]
25 | },
26 | "browserslist": {
27 | "production": [
28 | ">0.2%",
29 | "not dead",
30 | "not op_mini all"
31 | ],
32 | "development": [
33 | "last 1 chrome version",
34 | "last 1 firefox version",
35 | "last 1 safari version"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/antd4.x/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Playground 4.x
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/antd4.x/src/Demo.tsx:
--------------------------------------------------------------------------------
1 | import {useCallback, useMemo, useState} from "react";
2 | import copy from "copy-to-clipboard";
3 | import Form from "antd/es/form";
4 | import Alert from "antd/es/alert";
5 | import Button from "antd/es/button";
6 | import Switch from "antd/es/switch";
7 | import Card from "antd/es/card/Card";
8 | import Divider from "antd/es/divider";
9 | import {useForm} from "antd/es/form/Form";
10 | import PhoneInput from "antd-phone-input";
11 | import FormItem from "antd/es/form/FormItem";
12 | import Title from "antd/es/typography/Title";
13 | import Paragraph from "antd/es/typography/Paragraph";
14 | import SunOutlined from "@ant-design/icons/SunOutlined";
15 | import MoonOutlined from "@ant-design/icons/MoonOutlined";
16 | import CopyOutlined from "@ant-design/icons/CopyOutlined";
17 | import CheckOutlined from "@ant-design/icons/CheckOutlined";
18 |
19 | const Demo = () => {
20 | const [form] = useForm();
21 | const [value, setValue] = useState(null);
22 | const [arrow, setArrow] = useState(false);
23 | const [strict, setStrict] = useState(false);
24 | const [search, setSearch] = useState(false);
25 | const [copied, setCopied] = useState(false);
26 | const [dropdown, setDropdown] = useState(true);
27 | const [distinct, setDistinct] = useState(false);
28 | const [disabled, setDisabled] = useState(false);
29 | const [parentheses, setParentheses] = useState(true);
30 |
31 | const validator = useCallback((_: any, {valid}: any) => {
32 | if (valid(strict)) return Promise.resolve();
33 | return Promise.reject("Invalid phone number");
34 | }, [strict])
35 |
36 | const code = useMemo(() => {
37 | let code = "";
46 | return code;
47 | }, [distinct, disabled, arrow, search, dropdown, parentheses])
48 |
49 | const changeTheme = () => {
50 | const pathname = window.location.pathname.replace(/\/$/, '');
51 | if (pathname.endsWith("/dark")) {
52 | window.location.replace(pathname.replace('/dark', ''));
53 | } else {
54 | window.location.replace(pathname + "/dark");
55 | }
56 | }
57 |
58 | const handleFinish = ({phone}: any) => setValue(phone);
59 |
60 | return (
61 |
68 |
75 |
76 | Ant Phone Input Playground
77 |
78 |
79 | This is a playground for the Ant Phone Input component. You can change the settings and see how
80 | the component behaves. Also, see the code for the component and the value it returns.
81 |
82 | Settings
83 |
164 | Component
165 |
190 |
194 | If your application uses 5.x version of Ant Design, you should use this
195 | playground
197 | server to test the component.
198 | >}
199 | />
200 |
81 |
82 | Ant Phone Input Playground
83 |
84 |
85 | This is a playground for the Ant Phone Input component. You can change the settings and see how
86 | the component behaves. Also, see the code for the component and the value it returns.
87 |
88 | Settings
89 |
169 | Component
170 |
195 |
199 | If your application uses 4.x version of Ant Design, you should use this
200 | playground
202 | server to test the component.
203 | >}
204 | />
205 |