react-native-international-phone-number 0.8.7 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -42,6 +42,7 @@
42
42
  ## Features
43
43
 
44
44
  - 📱 Works with iOS, Android (Cross-platform), Expo and Web;
45
+ - ✅ Check phone number validation;
45
46
  - 🎨 Lib with UI customizable;
46
47
  - 🌎 Phone Input Mask according with the selected country;
47
48
  - 👨‍💻 Functional and class component support;
@@ -87,6 +88,8 @@
87
88
  - [Show Only Some Countries Inside Modal](#show-only-some-countries)
88
89
  - [Exclude some countries Inside Modal](#exclude-some-countries)
89
90
  - [Show Popular Countries at the Top of the Countries List Inside Modal](#show-popular-countries-at-the-top-of-the-countries-list-inside-modal)
91
+ - [Custom Modal Section Titles](#custom-modal-section-titles)
92
+ - [Hide the Modal Section Titles](#hide-the-modal-section-titles)
90
93
  - [Right to Left Input](#right-to-left-input)
91
94
  - [Dont allow Zero After Calling Code](#dont-allow-zero-after-calling-code)
92
95
  - [Lib Props](#component-props-phoneinputprops)
@@ -100,6 +103,7 @@
100
103
 
101
104
  ## Old Versions
102
105
 
106
+ - [Version 0.8.x](https://github.com/AstrOOnauta/react-native-international-phone-number/tree/v0.8.x)
103
107
  - [Version 0.7.x](https://github.com/AstrOOnauta/react-native-international-phone-number/tree/v0.7.x)
104
108
  - [Version 0.6.x](https://github.com/AstrOOnauta/react-native-international-phone-number/tree/v0.6.x)
105
109
  - [Version 0.5.x](https://github.com/AstrOOnauta/react-native-international-phone-number/tree/v0.5.x)
@@ -162,21 +166,6 @@ npx react-native-asset
162
166
 
163
167
  > Observation: you need to recompile your project after adding new fonts.
164
168
 
165
- - ### Using Web:
166
-
167
- Change the `app.json` file to:
168
-
169
- ```bash
170
- ...
171
-
172
- "web": {
173
- ...
174
- "output": "single",
175
- ...
176
- },
177
- ...
178
- ```
179
-
180
169
  <br>
181
170
 
182
171
  ## Basic Usage
@@ -186,7 +175,7 @@ Change the `app.json` file to:
186
175
  ```jsx
187
176
  import React from 'react';
188
177
  import { View, Text } from 'react-native';
189
- import PhoneInput from 'react-native-international-phone-number';
178
+ import PhoneInput, { isValidPhoneNumber } from 'react-native-international-phone-number';
190
179
 
191
180
  export class App extends React.Component {
192
181
  constructor(props) {
@@ -226,6 +215,12 @@ export class App extends React.Component {
226
215
  <Text>
227
216
  Phone Number: {`${this.state.selectedCountry?.callingCode} ${this.state.inputValue}`}
228
217
  </Text>
218
+ <Text>
219
+ isValid:{' '}
220
+ {isValidPhoneNumber(this.state.inputValue, this.state.selectedCountry)
221
+ ? 'true'
222
+ : 'false'}
223
+ </Text>
229
224
  </View>
230
225
  </View>
231
226
  );
@@ -238,7 +233,9 @@ export class App extends React.Component {
238
233
  ```jsx
239
234
  import React, { useState } from 'react';
240
235
  import { View, Text } from 'react-native';
241
- import PhoneInput from 'react-native-international-phone-number';
236
+ import PhoneInput, {
237
+ isValidPhoneNumber,
238
+ } from 'react-native-international-phone-number';
242
239
 
243
240
  export default function App() {
244
241
  const [selectedCountry, setSelectedCountry] = useState(null);
@@ -269,6 +266,12 @@ export default function App() {
269
266
  Phone Number:{' '}
270
267
  {`${selectedCountry?.callingCode} ${inputValue}`}
271
268
  </Text>
269
+ <Text>
270
+ isValid:{' '}
271
+ {isValidPhoneNumber(inputValue, selectedCountry)
272
+ ? 'true'
273
+ : 'false'}
274
+ </Text>
272
275
  </View>
273
276
  </View>
274
277
  );
@@ -282,6 +285,7 @@ import React, { useState } from 'react';
282
285
  import { View, Text } from 'react-native';
283
286
  import PhoneInput, {
284
287
  ICountry,
288
+ isValidPhoneNumber,
285
289
  } from 'react-native-international-phone-number';
286
290
 
287
291
  export default function App() {
@@ -314,6 +318,12 @@ export default function App() {
314
318
  Phone Number:{' '}
315
319
  {`${selectedCountry?.callingCode} ${inputValue}`}
316
320
  </Text>
321
+ <Text>
322
+ isValid:{' '}
323
+ {isValidPhoneNumber(inputValue, selectedCountry)
324
+ ? 'true'
325
+ : 'false'}
326
+ </Text>
317
327
  </View>
318
328
  </View>
319
329
  );
@@ -340,7 +350,7 @@ export default function App() {
340
350
  function onSubmitRef() {
341
351
  Alert.alert(
342
352
  'Intermediate Result',
343
- `${phoneInputRef.current?.selectedCountry?.callingCode} ${phoneInputRef.current?.value}`
353
+ `Country: ${inputRef.current?.selectedCountry?.name?.en} \nPhone Number: ${inputRef.current?.fullPhoneNumber} \nisValid: ${inputRef.current?.isValid}`
344
354
  );
345
355
  }
346
356
 
@@ -386,6 +396,7 @@ import React, { useState, useEffect } from 'react';
386
396
  import { View, Text, TouchableOpacity, Alert } from 'react-native';
387
397
  import PhoneInput, {
388
398
  ICountry,
399
+ isValidPhoneNumber,
389
400
  } from 'react-native-international-phone-number';
390
401
  import { Controller, FieldValues } from 'react-hook-form';
391
402
 
@@ -403,9 +414,15 @@ export default function App() {
403
414
  }
404
415
 
405
416
  function onSubmit(form: FormProps) {
417
+ const phoneNumber = `${selectedCountry?.callingCode} ${form.phoneNumber}`;
418
+ const isValid = isValidPhoneNumber(
419
+ form.phoneNumber,
420
+ selectedCountry as ICountry
421
+ );
422
+
406
423
  Alert.alert(
407
424
  'Advanced Result',
408
- `${selectedCountry?.callingCode} ${form.phoneNumber}`
425
+ `Country: ${selectedCountry?.name?.en} \nPhone Number: ${phoneNumber} \nisValid: ${isValid}`
409
426
  );
410
427
  }
411
428
 
@@ -545,6 +562,10 @@ export default function App() {
545
562
  countryName: {
546
563
  color: '#F3F3F3',
547
564
  },
565
+ sectionTitle: {
566
+ marginVertical: 10,
567
+ color: '#F3F3F3',
568
+ }
548
569
  }}
549
570
  />
550
571
  ...
@@ -693,6 +714,17 @@ export default function App() {
693
714
 
694
715
  - ### Show Popular Countries at the Top of the Countries List Inside Modal:
695
716
 
717
+ ```jsx
718
+ ...
719
+ <PhoneInput
720
+ ...
721
+ popularCountriess={['BR', 'PT', 'CA', 'US']}
722
+ />
723
+ ...
724
+ ```
725
+
726
+ - ### Custom Modal Section Titles:
727
+
696
728
  ```jsx
697
729
  ...
698
730
  <PhoneInput
@@ -704,6 +736,18 @@ export default function App() {
704
736
  ...
705
737
  ```
706
738
 
739
+ - ### Hide the Modal Section Titles:
740
+
741
+ ```jsx
742
+ ...
743
+ <PhoneInput
744
+ ...
745
+ popularCountriess={['BR', 'PT', 'CA', 'US']}
746
+ modalSectionTitleDisabled
747
+ />
748
+ ...
749
+ ```
750
+
707
751
  - ### Right to Left Input:
708
752
 
709
753
  ```jsx
@@ -745,6 +789,7 @@ export default function App() {
745
789
  - `popularCountries?:` [ICountryCca2[]](lib/interfaces/countryCca2.ts);
746
790
  - `popularCountriesSectionTitle?:` string;
747
791
  - `restOfCountriesSectionTitle?:` string;
792
+ - `modalSectionTitleDisabled?:` boolean;
748
793
  - `rtl?:` boolean;
749
794
  - `disabled?:` boolean;
750
795
  - `modalDisabled?:` boolean;
@@ -758,7 +803,7 @@ export default function App() {
758
803
  - `modalNotFoundCountryMessage?:` string;
759
804
  - `customCaret?:` [ReactNode](https://reactnative.dev/docs/react-node);
760
805
  - `ref?:` [Ref](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/663f439d11d78b65f1dfd38d120f3728ea2cc207/types/react/index.d.ts#L100)<[IPhoneInputRef](lib/interfaces/phoneInputRef.ts)>;
761
- - `allowZeroAfterCallingCode?:` boolean
806
+ - `allowZeroAfterCallingCode?:` boolean.
762
807
 
763
808
  <br>
764
809
 
@@ -769,6 +814,7 @@ export default function App() {
769
814
  - `getCountryByCca2:` (cca2: string) => [ICountry](lib/interfaces/country.ts) | undefined;
770
815
  - `getCountriesByName:` (name: string, language: [ILanguage](lib/interfaces/language.ts)) => [ICountry](lib/interfaces/country.ts)[] | undefined;
771
816
  - `getCountryByPhoneNumber:` (phoneNumber: string) => [ICountry](lib/interfaces/country.ts) | undefined;
817
+ - `isValidPhoneNumber:` (phoneNumber: string, country: [ICountry](lib/interfaces/country.ts)) => boolean.
772
818
 
773
819
  </br>
774
820
 
Binary file
Binary file
@@ -933,7 +933,7 @@ export const countries = [
933
933
  zh: '巴西',
934
934
  tr: 'Brezilya',
935
935
  },
936
- phoneMasks: ['## ##### ####'],
936
+ phoneMasks: ['## #### ####', '## ##### ####'],
937
937
  },
938
938
  {
939
939
  callingCode: '+1',
package/lib/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { JSX } from 'react';
2
+
1
3
  import { ITheme } from './interfaces/theme';
2
4
  import { ICountry } from './interfaces/country';
3
5
  import { ILanguage } from './interfaces/language';
@@ -23,6 +25,11 @@ declare function getCountriesByName(
23
25
  language: ILanguage
24
26
  ): ICountry[] | undefined;
25
27
 
28
+ declare function isValidPhoneNumber(
29
+ phoneNumber: string,
30
+ country: ICountry
31
+ ): boolean;
32
+
26
33
  export default PhoneInput;
27
34
 
28
35
  export {
@@ -31,6 +38,7 @@ export {
31
38
  getCountryByCca2,
32
39
  getCountriesByCallingCode,
33
40
  getCountriesByName,
41
+ isValidPhoneNumber,
34
42
  ITheme,
35
43
  ILanguage,
36
44
  ICountry,
package/lib/index.js CHANGED
@@ -21,9 +21,12 @@ import getCountriesByName from './utils/getCountriesByName';
21
21
  import getCountriesByCallingCode from './utils/getCountriesByCallingCode';
22
22
  import getCountryByCca2 from './utils/getCountryByCca2';
23
23
  import getCountryByPhoneNumber from './utils/getCountryByPhoneNumber';
24
+ import isValidPhoneNumber from './utils/isValidPhoneNumber';
24
25
  import {
25
26
  getCountryNotFoundMessage,
26
27
  getPhoneNumberInputPlaceholder,
28
+ getPopularCountriesSectionTitle,
29
+ getRestOfCountriesSectionTitle,
27
30
  getSearchInputPlaceholder,
28
31
  } from './utils/getPlaceholders';
29
32
  import {
@@ -35,8 +38,8 @@ import {
35
38
  getFlagStyle,
36
39
  getFlagTextStyle,
37
40
  getInputStyle,
41
+ getModalSectionTitleStyle,
38
42
  } from './utils/getStyles';
39
- import styles from './styles';
40
43
 
41
44
  const PhoneInput = forwardRef(
42
45
  (
@@ -62,6 +65,7 @@ const PhoneInput = forwardRef(
62
65
  popularCountries,
63
66
  popularCountriesSectionTitle,
64
67
  restOfCountriesSectionTitle,
68
+ modalSectionTitleDisabled,
65
69
  modalSearchInputPlaceholder,
66
70
  modalSearchInputPlaceholderTextColor,
67
71
  modalSearchInputSelectionColor,
@@ -94,6 +98,7 @@ const PhoneInput = forwardRef(
94
98
  fullPhoneNumber: `${countryValue?.callingCode} ${inputValue}`,
95
99
  getSelectedCountry: () => countryValue,
96
100
  selectedCountry: countryValue,
101
+ isValid: isValidPhoneNumber(inputValue, selectedCountry),
97
102
  props: {
98
103
  theme,
99
104
  language,
@@ -114,6 +119,9 @@ const PhoneInput = forwardRef(
114
119
  showOnly,
115
120
  excludedCountries,
116
121
  popularCountries,
122
+ popularCountriesSectionTitle,
123
+ restOfCountriesSectionTitle,
124
+ modalSectionTitleDisabled,
117
125
  modalSearchInputPlaceholder,
118
126
  modalSearchInputPlaceholderTextColor,
119
127
  modalSearchInputSelectionColor,
@@ -137,6 +145,7 @@ const PhoneInput = forwardRef(
137
145
  fullPhoneNumber: `${country?.callingCode} ${phoneNumber}`,
138
146
  getSelectedCountry: () => country,
139
147
  selectedCountry: country,
148
+ isValid: isValidPhoneNumber(phoneNumber, country),
140
149
  props: {
141
150
  ...refBase.props,
142
151
  value: phoneNumber,
@@ -334,7 +343,10 @@ const PhoneInput = forwardRef(
334
343
  // Create a separate constant for each part of the component
335
344
  const touchableStart = (
336
345
  <>
337
- <Text testID={`${testID}-flag`} style={getFlagStyle(phoneInputStyles?.flag)}>
346
+ <Text
347
+ testID={`${testID}-flag`}
348
+ style={getFlagStyle(phoneInputStyles?.flag)}
349
+ >
338
350
  {countryValue?.flag}
339
351
  </Text>
340
352
  {customCaret || (
@@ -479,8 +491,18 @@ const PhoneInput = forwardRef(
479
491
  ListHeaderComponent={({ countries, lang, onPress }) => {
480
492
  return (
481
493
  <View>
482
- {popularCountriesSectionTitle && (
483
- <Text>{popularCountriesSectionTitle}</Text>
494
+ {!modalSectionTitleDisabled && (
495
+ <Text
496
+ style={getModalSectionTitleStyle(
497
+ theme,
498
+ modalStyles
499
+ )}
500
+ >
501
+ {popularCountriesSectionTitle ||
502
+ getPopularCountriesSectionTitle(
503
+ language || 'en'
504
+ )}
505
+ </Text>
484
506
  )}
485
507
  {countries.map((country, index) => {
486
508
  return (
@@ -497,9 +519,17 @@ const PhoneInput = forwardRef(
497
519
  />
498
520
  );
499
521
  })}
500
- {restOfCountriesSectionTitle && (
501
- <Text style={styles.sectionTitle}>
502
- {restOfCountriesSectionTitle}
522
+ {!modalSectionTitleDisabled && (
523
+ <Text
524
+ style={getModalSectionTitleStyle(
525
+ theme,
526
+ modalStyles
527
+ )}
528
+ >
529
+ {restOfCountriesSectionTitle ||
530
+ getRestOfCountriesSectionTitle(
531
+ language || 'en'
532
+ )}
503
533
  </Text>
504
534
  )}
505
535
  </View>
@@ -521,4 +551,5 @@ export {
521
551
  getCountryByCca2,
522
552
  getCountriesByCallingCode,
523
553
  getCountriesByName,
554
+ isValidPhoneNumber,
524
555
  };
@@ -23,4 +23,6 @@ export interface IModalStyles {
23
23
  callingCode?: StyleProp<TextStyle>;
24
24
  // Country name styles [Text]
25
25
  countryName?: StyleProp<TextStyle>;
26
+ // Popular and rest of countries titles styles [Text]
27
+ sectionTitle?: StyleProp<TextStyle>;
26
28
  }
@@ -28,6 +28,7 @@ interface BasePhoneInput extends TextInputProps {
28
28
  popularCountries?: Array<ICountryCca2>;
29
29
  popularCountriesSectionTitle?: string;
30
30
  restOfCountriesSectionTitle?: string;
31
+ modalSectionTitleDisabled?: boolean;
31
32
  modalSearchInputPlaceholder?: string;
32
33
  modalSearchInputPlaceholderTextColor?: string;
33
34
  modalSearchInputSelectionColor?: string;
@@ -13,4 +13,5 @@ export interface IPhoneInputRef extends TextInput {
13
13
  fullPhoneNumber: string;
14
14
  getSelectedCountry: () => ICountry;
15
15
  selectedCountry: ICountry;
16
+ isValid: boolean;
16
17
  }
package/lib/styles.js CHANGED
@@ -50,6 +50,10 @@ const inputBase = {
50
50
  fontSize: 16,
51
51
  };
52
52
 
53
+ const modalSectionTitleBase = {
54
+ marginVertical: 10,
55
+ };
56
+
53
57
  const styles = StyleSheet.create({
54
58
  lightContainer: {
55
59
  ...containerBase,
@@ -75,7 +79,7 @@ const styles = StyleSheet.create({
75
79
  marginRight: 6,
76
80
  fontFamily:
77
81
  Platform.OS === 'web'
78
- ? navigator?.userAgent?.includes('Win')
82
+ ? typeof navigator !== 'undefined' && navigator?.userAgent?.includes('Win')
79
83
  ? 'TwemojiMozilla'
80
84
  : 'System'
81
85
  : 'System',
@@ -112,9 +116,13 @@ const styles = StyleSheet.create({
112
116
  ...inputBase,
113
117
  color: '#F3F3F3',
114
118
  },
115
- sectionTitle:{
116
- marginTop: 10,
117
- marginBottom: 10,
119
+ lightModalSectionTitle: {
120
+ ...modalSectionTitleBase,
121
+ color: '#0A0A0A',
122
+ },
123
+ darkModalSectionTitle: {
124
+ ...modalSectionTitleBase,
125
+ color: '#F3F3F3',
118
126
  },
119
127
  lightCountryPicker: {
120
128
  modal: {
@@ -146,7 +154,7 @@ const styles = StyleSheet.create({
146
154
  fontSize: 20,
147
155
  fontFamily:
148
156
  Platform.OS === 'web'
149
- ? navigator?.userAgent?.includes('Win')
157
+ ? typeof navigator !== 'undefined' && navigator?.userAgent?.includes('Win')
150
158
  ? 'TwemojiMozilla'
151
159
  : 'System'
152
160
  : 'System',
@@ -187,7 +195,7 @@ const styles = StyleSheet.create({
187
195
  fontSize: 20,
188
196
  fontFamily:
189
197
  Platform.OS === 'web'
190
- ? navigator?.userAgent?.includes('Win')
198
+ ? typeof navigator !== 'undefined' && navigator?.userAgent?.includes('Win')
191
199
  ? 'TwemojiMozilla'
192
200
  : 'System'
193
201
  : 'System',
@@ -132,3 +132,93 @@ export function getCountryNotFoundMessage(language) {
132
132
  return 'Herhangi bir ülke bulunamadı';
133
133
  }
134
134
  }
135
+
136
+ export function getPopularCountriesSectionTitle(language) {
137
+ switch (language) {
138
+ case 'en':
139
+ return 'Suggested countries';
140
+ case 'ru':
141
+ return 'Предложенные страны';
142
+ case 'pl':
143
+ return 'Sugerowane kraje';
144
+ case 'ua':
145
+ return 'Рекомендовані країни';
146
+ case 'cz':
147
+ return 'Doporučené země';
148
+ case 'by':
149
+ return 'Прапанаваныя краіны';
150
+ case 'pt':
151
+ return 'Países sugeridos';
152
+ case 'es':
153
+ return 'Países sugeridos';
154
+ case 'ro':
155
+ return 'Țări sugerate';
156
+ case 'bg':
157
+ return 'Предложени държави';
158
+ case 'de':
159
+ return 'Vorgeschlagene Länder';
160
+ case 'fr':
161
+ return 'Pays suggérés';
162
+ case 'nl':
163
+ return 'Voorgestelde landen';
164
+ case 'it':
165
+ return 'Paesi suggeriti';
166
+ case 'cn':
167
+ return '推荐国家';
168
+ case 'ee':
169
+ return 'Soovitatud riigid';
170
+ case 'jp':
171
+ return 'おすすめの国';
172
+ case 'he':
173
+ return 'מדינות מוצעות';
174
+ case 'ar':
175
+ return 'الدول المقترحة';
176
+ case 'tr':
177
+ return 'Önerilen ülkeler';
178
+ }
179
+ }
180
+
181
+ export function getRestOfCountriesSectionTitle(language) {
182
+ switch (language) {
183
+ case 'en':
184
+ return 'All';
185
+ case 'ru':
186
+ return 'Все';
187
+ case 'pl':
188
+ return 'Wszystko';
189
+ case 'ua':
190
+ return 'Все';
191
+ case 'cz':
192
+ return 'Vše';
193
+ case 'by':
194
+ return 'Усё';
195
+ case 'pt':
196
+ return 'Tudo';
197
+ case 'es':
198
+ return 'Todo';
199
+ case 'ro':
200
+ return 'Toate';
201
+ case 'bg':
202
+ return 'Всички';
203
+ case 'de':
204
+ return 'Alle';
205
+ case 'fr':
206
+ return 'Tout';
207
+ case 'nl':
208
+ return 'Alle';
209
+ case 'it':
210
+ return 'Tutto';
211
+ case 'cn':
212
+ return '全部';
213
+ case 'ee':
214
+ return 'Kõik';
215
+ case 'jp':
216
+ return 'すべて';
217
+ case 'he':
218
+ return 'הכל';
219
+ case 'ar':
220
+ return 'الكل';
221
+ case 'tr':
222
+ return 'Tümü';
223
+ }
224
+ }
@@ -70,6 +70,15 @@ export function getInputStyle(theme, inputStyle) {
70
70
  ];
71
71
  }
72
72
 
73
+ export function getModalSectionTitleStyle(theme, modalStyle) {
74
+ return [
75
+ theme === 'dark'
76
+ ? styles.darkModalSectionTitle
77
+ : styles.lightModalSectionTitle,
78
+ modalStyle?.sectionTitle ? modalStyle.sectionTitle : {},
79
+ ];
80
+ }
81
+
73
82
  export function getCountryPickerStyle(theme, height, modalStyle) {
74
83
  const themeStyle =
75
84
  theme === 'dark'
@@ -0,0 +1,9 @@
1
+ import {phone} from 'phone';
2
+
3
+ export default function isValidPhoneNumber(phoneNumber, country) {
4
+ const isValid = phone(`${country?.callingCode} ${phoneNumber}`, {
5
+ country: country?.cca2,
6
+ }).isValid;
7
+
8
+ return isValid;
9
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-international-phone-number",
3
3
  "author": "AstrOOnauta (https://github.com/AstrOOnauta)",
4
- "version": "0.8.7",
4
+ "version": "0.9.0",
5
5
  "description": "International mobile phone input component with mask for React Native",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -44,7 +44,8 @@
44
44
  "react-native": "*"
45
45
  },
46
46
  "dependencies": {
47
- "react-native-country-codes-picker": "2.3.5"
47
+ "react-native-country-codes-picker": "2.3.5",
48
+ "phone": "3.1.58"
48
49
  },
49
50
  "devDependencies": {
50
51
  "metro-react-native-babel-preset": "^0.61.0"