Skip to content

Commit cdfb81d

Browse files
Release v0.2.0 [optimizations and bugfixes] (GH-43)
2 parents ba3a8f5 + 7374490 commit cdfb81d

File tree

16 files changed

+256
-141
lines changed

16 files changed

+256
-141
lines changed

.github/workflows/tests.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,22 @@ jobs:
1111

1212
runs-on: ubuntu-latest
1313

14+
strategy:
15+
matrix:
16+
node-version: [ 16.x, 18.x, 20.x ]
17+
1418
steps:
1519
- name: Checkout
1620
uses: actions/checkout@v3
1721

22+
- name: Set up Node
23+
uses: actions/setup-node@v3
24+
with:
25+
node-version: ${{ matrix.node-version }}
26+
registry-url: https://registry.npmjs.org/
27+
1828
- name: Install dependencies
19-
run: yarn && yarn install
29+
run: npm install
2030

2131
- name: Run tests
22-
run: yarn test
32+
run: npm test

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,24 @@ The value of the component is an object containing the parts of a phone number.
6969
of opportunities for handling the data in your custom way. For example, you can easily merge the parts of the phone
7070
number into a single string.
7171

72-
```json
72+
```javascript
7373
{
74-
"countryCode": 1,
75-
"areaCode": 702,
76-
"phoneNumber": "1234567",
77-
"isoCode": "us",
78-
"valid": true
74+
countryCode: 1,
75+
areaCode: 702,
76+
phoneNumber: "1234567",
77+
isoCode: "us",
78+
valid: function valid()
7979
}
8080
```
8181
8282
## Validation
8383
84-
The `valid` property of the value object shows the real-time validity of the phone number depending on the country. So
84+
The `valid` function of the value object returns the validity of the phone number depending on the selected country. So
8585
this can be used in a `validator` like this:
8686
8787
```javascript
8888
const validator = (_, {valid}) => {
89-
if (valid) {
89+
if (valid()) {
9090
return Promise.resolve();
9191
}
9292
return Promise.reject("Invalid phone number");
@@ -104,7 +104,7 @@ return (
104104
| Property | Description | Type |
105105
|--------------------|---------------------------------------------------------------------------------------------------------------------------------|---------------------|
106106
| size | Either `large`, `middle` or `small`. Default value is `middle`. See at ant [docs][antInputProps] for more. | string |
107-
| value | An object containing the parts of phone number. E.g. `value={{countryCode: 1, areaCode: 702, phoneNumber: "1234567"}}`. | object |
107+
| value | An object containing the parts of phone number. E.g. `value={{countryCode: 1, areaCode: 702, phoneNumber: "1234567"}}`. | [object](#value) |
108108
| style | Applies CSS styles to the container element. | CSSProperties |
109109
| className | The value will be assigned to the container element. | string |
110110
| disabled | Disables the whole input component. | boolean |

jestconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
"moduleNameMapper": {
77
"^.+\\.((?:c|le|s[ca])ss)$": "identity-obj-proxy"
88
},
9+
"modulePathIgnorePatterns": [
10+
"<rootDir>/examples"
11+
],
912
"moduleFileExtensions": [
1013
"ts",
1114
"tsx",

package.json

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "0.1.8",
2+
"version": "0.2.0",
33
"name": "antd-phone-input",
44
"description": "Advanced, highly customizable phone input component for Ant Design.",
55
"keywords": [
@@ -26,34 +26,44 @@
2626
},
2727
"exports": {
2828
".": {
29-
"import": "./index.esm.js",
29+
"import": "./index.js",
3030
"require": "./index.cjs.js",
3131
"types": {
3232
"default": "./index.d.ts"
3333
}
3434
},
3535
"./legacy": {
36-
"import": "./legacy/index.esm.js",
36+
"import": "./legacy/index.js",
3737
"require": "./legacy/index.cjs.js",
3838
"types": {
3939
"default": "./legacy/index.d.ts"
4040
}
4141
},
42+
"./types": {
43+
"import": "./types.js",
44+
"require": "./types.cjs.js",
45+
"types": {
46+
"default": "./types.d.ts"
47+
}
48+
},
4249
"./legacy/style": {
4350
"default": "./legacy/style.less"
4451
},
4552
"./package.json": "./package.json"
4653
},
4754
"files": [
4855
"index*",
56+
"style*",
57+
"types*",
4958
"legacy",
5059
"LICENSE",
5160
"README.md"
5261
],
5362
"scripts": {
54-
"build": "npm run build:clean && npm run build:rollup && cp src/legacy/*.less legacy",
55-
"build:rollup": "rollup -c --configPlugin @rollup/plugin-typescript",
56-
"build:clean": "rm -r legacy index* || true",
63+
"rename": "bash -c 'for file in $1*.js; do if [[ \"${file%.js}\" =~ ^[^.]*$ ]]; then mv \"$file\" \"${file%.js}.$0.js\"; fi; done'",
64+
"compile": "tsc --module commonjs && npm run rename -- cjs && npm run rename -- cjs legacy/ && tsc --declaration",
65+
"build": "npm run compile && tsx scripts/prepare-styles.ts",
66+
"prebuild": "rm -r legacy index* style* types* || true",
5767
"test": "jest --config jestconfig.json"
5868
},
5969
"license": "MIT",
@@ -62,28 +72,19 @@
6272
"react": ">=16"
6373
},
6474
"devDependencies": {
65-
"@rollup/plugin-alias": "^4.0.3",
66-
"@rollup/plugin-json": "^6.0.0",
67-
"@rollup/plugin-typescript": "^11.0.0",
6875
"@testing-library/react": "^14.0.0",
69-
"@testing-library/user-event": "^14.4.3",
70-
"@types/jest": "^29.4.0",
71-
"@types/node": "^18.14.1",
72-
"@types/react": "^18.0.28",
73-
"antd": "npm:antd@^5.3.2",
76+
"@testing-library/user-event": "^14.5.1",
77+
"@types/jest": "^29.5.5",
78+
"@types/react": "^18.2.21",
79+
"antd": "npm:[email protected]",
7480
"antd4": "npm:antd@^4.24.8",
7581
"identity-obj-proxy": "^3.0.0",
76-
"jest": "^29.4.3",
77-
"jest-environment-jsdom": "^29.4.3",
78-
"postcss": "^8.4.21",
79-
"react": "^18.2.0",
80-
"react-dom": "^18.2.0",
81-
"rollup": "3.17.2",
82-
"rollup-plugin-dts": "^5.2.0",
83-
"rollup-plugin-postcss": "^4.0.2",
84-
"ts-jest": "^29.0.5",
85-
"tslib": "^2.5.0",
86-
"typescript": "^4.9.5"
82+
"jest": "^29.7.0",
83+
"jest-environment-jsdom": "^29.7.0",
84+
"ts-jest": "^29.1.1",
85+
"tslib": "^2.6.2",
86+
"tsx": "^3.12.10",
87+
"typescript": "^5.2.2"
8788
},
8889
"dependencies": {
8990
"react-phone-input-2": "^2.15.1"

rollup.config.ts

Lines changed: 0 additions & 45 deletions
This file was deleted.

scripts/prepare-styles.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import fs from "fs";
2+
import util from "node:util";
3+
import process from "node:child_process";
4+
5+
const exec = (command: string) => util.promisify(process.exec)(command);
6+
7+
(async () => {
8+
await exec("bash -c 'cp src/legacy/*.{le,c}ss legacy'");
9+
10+
const style = fs.readFileSync("node_modules/react-phone-input-2/lib/style.css");
11+
const regex = /(\.react-tel-input)\s\.flag.+\1\s*\.\w{2}\b\s*\{[^{}]*}/;
12+
const flags = "\n" + (style.toString().match(regex) || [""])[0];
13+
14+
let styles = fs.readFileSync("./legacy/style5.css", "utf8") + flags;
15+
styles = styles.replaceAll(/\/\*\*.*?\*\/[\n\s]*/gs, "");
16+
styles = styles.replaceAll(/\B[^{}]*?\{[\s\n]}/g, "");
17+
styles = styles.replaceAll(/\//g, "\\\/");
18+
styles = styles.replaceAll(/\n\s*/g, "");
19+
20+
await exec(`find legacy -maxdepth 1 -name '*.js' -type f -exec sed -i 's/style5.css/${styles}/g' {} +`);
21+
await exec("find . legacy -maxdepth 1 \\( -name '*.ts' -o -name '*.js' \\) -type f -exec sed -i 's/antd\\/lib/antd\\/es/g' {} +");
22+
})();

scripts/tsconfig.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"noUnusedLocals": true,
4+
"noUnusedParameters": true,
5+
"esModuleInterop": true,
6+
"lib": [
7+
"esnext"
8+
]
9+
}
10+
}

src/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import {useContext, useMemo} from "react";
2-
import useToken from "antd/lib/theme/useToken";
32
import genComponentStyleHook from "antd/lib/input/style";
43
import {ConfigContext} from "antd/lib/config-provider";
5-
import {useStyleRegister} from "antd/lib/theme/internal";
64
import {FormItemInputContext} from "antd/lib/form/context";
75
import {getStatusClassNames} from "antd/lib/_util/statusUtils";
6+
import {useStyleRegister, useToken} from "antd/lib/theme/internal";
87

98
import InputLegacy from "./legacy";
109
import genPhoneInputStyle from "./style";

src/legacy/index.tsx

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import {useMemo, useState} from "react";
1+
import {useCallback, useEffect, useMemo, useRef, useState} from "react";
22
import ReactPhoneInput from "react-phone-input-2";
33

4-
import {ParsePhoneNumber, PhoneInputProps, ReactPhoneOnChange, ReactPhoneOnMount} from "../types";
5-
6-
import "./style5.css";
4+
import {CountryData, PhoneInputProps, PhoneNumber, ReactPhoneOnChange, ReactPhoneOnMount} from "../types";
75

6+
import styleInject from "./style";
87
import masks from "./phoneMasks.json";
98
import timezones from "./timezones.json";
109
import validations from "./validations.json";
1110

11+
styleInject("style5.css");
12+
13+
let browserLoaded = true;
14+
1215
type ISO2Code = keyof typeof masks;
1316
type Timezone = keyof typeof timezones;
1417

@@ -18,7 +21,7 @@ const getDefaultISO2Code = () => {
1821
return (timezones[timezone] || "").toLowerCase() || "us";
1922
}
2023

21-
const parsePhoneNumber: ParsePhoneNumber = (value, data, formattedNumber) => {
24+
const parsePhoneNumber = (value: string, data: CountryData, formattedNumber: string): PhoneNumber => {
2225
const isoCode = data?.countryCode;
2326
const countryCodePattern = /\+\d+/;
2427
const areaCodePattern = /\((\d+)\)/;
@@ -42,14 +45,7 @@ const parsePhoneNumber: ParsePhoneNumber = (value, data, formattedNumber) => {
4245
const phoneNumberMatch = value ? (value.match(phoneNumberPattern) || []) : [];
4346
const phoneNumber = phoneNumberMatch.length > 1 ? phoneNumberMatch[1] : null;
4447

45-
/** Checks if both the area code and phone number length satisfy the validation rules */
46-
const rules = validations[isoCode as ISO2Code] || {areaCode: [], phoneNumber: []};
47-
const valid = [
48-
rules.areaCode.includes((areaCode || "").toString().length),
49-
rules.phoneNumber.includes((phoneNumber || "").toString().length),
50-
].every(Boolean);
51-
52-
return {countryCode, areaCode, phoneNumber, isoCode, valid, dialChanged};
48+
return {countryCode, areaCode, phoneNumber, isoCode, dialChanged};
5349
}
5450

5551
const PhoneInput = ({
@@ -64,6 +60,9 @@ const PhoneInput = ({
6460
inputClass: inputClassProxy,
6561
...reactPhoneInputProps
6662
}: PhoneInputProps) => {
63+
const loaded = useRef(browserLoaded);
64+
const reset = useRef(false);
65+
const initialized = useRef(false);
6766
const [currentCode, setCurrentCode] = useState("");
6867

6968
const countryCode = useMemo(() => country || getDefaultISO2Code(), [country]);
@@ -79,29 +78,50 @@ const PhoneInput = ({
7978
return inputClassProxy ? `${className} ${inputClassProxy}` : className;
8079
}, [inputClassProxy, size]);
8180

82-
const onChange: ReactPhoneOnChange = (value, data, event, formattedNumber) => {
81+
const checkValidity = (metadata: PhoneNumber) => {
82+
/** Checks if both the area code and phone number length satisfy the validation rules */
83+
const rules = validations[metadata.isoCode as ISO2Code] || {areaCode: [], phoneNumber: []};
84+
const isValid = reset.current || ((loaded.current || initialized.current) ? [
85+
rules.areaCode.includes((metadata.areaCode || "").toString().length),
86+
rules.phoneNumber.includes((metadata.phoneNumber || "").toString().length),
87+
].every(Boolean) : !initialized.current);
88+
initialized.current = true;
89+
loaded.current = false;
90+
reset.current = false;
91+
return isValid;
92+
}
93+
94+
const onChange: ReactPhoneOnChange = useCallback((value, data, event, formattedNumber) => {
8395
const {dialChanged, ...metadata} = parsePhoneNumber(value, data, formattedNumber);
8496
const code = metadata.isoCode as ISO2Code;
8597

8698
if (code !== currentCode) {
8799
/** Clears phone number when the country is selected manually */
88100
metadata.areaCode = dialChanged ? null : metadata.areaCode;
89101
metadata.phoneNumber = null;
90-
metadata.valid = false;
91102
setCurrentCode(code);
92103
}
93104

94-
handleChange(metadata, event);
95-
}
105+
handleChange({...metadata, valid: () => checkValidity(metadata)}, event);
106+
}, [currentCode, handleChange]);
96107

97-
const onMount: ReactPhoneOnMount = (rawValue, {countryCode, ...event}, formattedNumber) => {
108+
const onMount: ReactPhoneOnMount = useCallback((rawValue, {countryCode, ...event}, formattedNumber) => {
98109
const {dialChanged, ...metadata} = parsePhoneNumber(rawValue, {countryCode}, formattedNumber);
99-
/** Initiates the current country code with the code of initial value */
100-
setCurrentCode(metadata.isoCode as ISO2Code);
101110
/** Initializes the existing value */
102-
handleChange(metadata, event);
103-
handleMount(metadata);
104-
}
111+
handleChange({...metadata, valid: () => checkValidity(metadata)}, event);
112+
handleMount({...metadata, valid: () => checkValidity(metadata)});
113+
/** Sets the current country code to the code of the initial value */
114+
setCurrentCode(metadata.isoCode as ISO2Code);
115+
if (loaded.current && !initialized.current) reset.current = true;
116+
initialized.current = false;
117+
}, [handleChange, handleMount]);
118+
119+
useEffect(() => {
120+
reset.current = !browserLoaded;
121+
return () => {
122+
browserLoaded = false;
123+
}
124+
}, []);
105125

106126
return (
107127
<ReactPhoneInput

src/legacy/phoneMasks.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@
153153
"mw": "(.) .... ....",
154154
"mx": "(...) ... ....",
155155
"my": "(...) ... ....",
156-
"mz": "(..) ... ...",
156+
"mz": "(..) ... ....",
157157
"na": "(..) ... ....",
158158
"nc": "(..) ....",
159159
"ne": "(..) .. ....",

0 commit comments

Comments
 (0)