Skip to content

Commit

Permalink
Merge pull request #11 from kuasha420/prepare-release
Browse files Browse the repository at this point in the history
Prepare new release.
  • Loading branch information
kuasha420 committed Jul 29, 2024
2 parents e9c21b8 + 8e0c89f commit 5d43ae3
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 60 deletions.
38 changes: 25 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
- Looks and feels consistent with React Native Paper.
- Allows specifying default country.
- Allows specifying a list of countries to show on top of the list.
- Allows user to specify which countries to show.
- Allows user to specify which countries to show or hide in the list.
- Exposes imperative methods to open and close the country code picker.
- Supports light and dark themes.
- Works well on Android, iOS and Web.
Expand Down Expand Up @@ -121,6 +121,10 @@ Import the `PhoneNumberInput` component from the library. You can then use it in
import React, { useState } from 'react';
import { PhoneNumberInput, getCountryByCode } from 'react-native-paper-phone-number-input';

// `showFirstOnList`, `includeCountries` and `excludeCountries` should be defined outsude
// the component to prevent uncessary recomputations and re-renders.
const includeCountries = ['AZ', 'BD', 'CA', 'GB', 'IN', 'NZ', 'US', 'TR'];

export default function App() {
const [countryCode, setCountryCode] = useState<string>('BD'); // Default country code
const [phoneNumber, setPhoneNumber] = useState<string>();
Expand All @@ -133,7 +137,7 @@ export default function App() {
setCode={setCountryCode}
phoneNumber={phoneNumber}
setPhoneNumber={setPhoneNumber}
onlyCountries={['AZ', 'BD', 'CA', 'GB', 'IN', 'NZ', 'US', 'TR']}
includeCountries={includeCountries}
/>
);
}
Expand All @@ -147,17 +151,25 @@ A more complete example can be found in the `example` directory.

#### Props

| Prop | Type | Description | Notes |
| --------------------- | ------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `code` | `string` | The country code. | Optional. By default, the country code is set to `##` which shows a world icon. |
| `setCode` | `(code: string) => void` | A function that sets the country code. | Required. |
| `phoneNumber` | `string` | The phone number. | Optional. By default, no phone number is set. |
| `setPhoneNumber` | `(phoneNumber: string) => void` | A function that sets the phone number. | Required. |
| `showFirstOnList` | `string[]` | A list of country codes that should be shown on top of the list. | Optional. By default, countries are shown alphabetically. |
| `modalStyle` | `StyleProp<ViewStyle>` | The style of the modal that shows the country code picker. | Optional. |
| `modalContainerStyle` | `StyleProp<ViewStyle>` | The style of the container of the modal that shows the country code picker. | Optional. |
| `onlyCountries` | `string[]` | A list of country codes that specifies which countries can be selected. | Optional. |
| `...rest` | `...TextInputProps` | Any other props that you want to pass to the `TextInput` component of React Native Paper. | Optional. |
| Prop | Type | Description | Notes |
| --------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| `code` | `string` | The country code. | Optional. By default, the country code is set to `##` which shows a world icon. |
| `setCode` | `(code: string) => void` | A function that sets the country code. | Required. |
| `phoneNumber` | `string` | The phone number. | Optional. By default, no phone number is set. |
| `setPhoneNumber` | `(phoneNumber: string) => void` | A function that sets the phone number. | Required. |
| `showFirstOnList` | `string[]` | A list of country codes that should be shown on top of the list. | Optional. By default, countries are shown alphabetically. |
| `includeCountries` | `string[]` | A list of country codes that specifies which countries can be selected. | Optional. By default, shows all countries. |
| `excludeCountries` | `string[]` | A list of country codes that specifies which countries cannot be selected. | Optional. By default, does not exclude any countries. |
| `limitMaxLength` | `boolean` | Limit the maximum length of the phone number for the country as defined in [E.164](https://en.wikipedia.org/wiki/E.164). | Optional. By default, the maximum length of the phone number is not limited. |
| `modalStyle` | `StyleProp<ViewStyle>` | The style of the modal that shows the country code picker. | Optional. |
| `modalContainerStyle` | `StyleProp<ViewStyle>` | The style of the container of the modal that shows the country code picker. | Optional. |
| `...rest` | `...TextInputProps` | Any other props that you want to pass to the `TextInput` component of React Native Paper. | Optional. |

> [!TIP]
> The props that accepts a list o f country codes such as `showFirstOnList`, `includeCountries` and `excludeCountries` should be defined outside the component or memoized to prevent unnecessary recomputations and re-renders!
> [!CAUTION]
> If you set contradictory prop values in `includeCountries` and `excludeCountries` props, the `excludeCountries` prop will take precedence over the `includeCountries` prop. ie. If you set the same country code in both `includeCountries` and `excludeCountries`, the country will be excluded.
#### Ref Methods

Expand Down
4 changes: 1 addition & 3 deletions example/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"assetBundlePatterns": [
"**/*"
],
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": true,
"userInterfaceStyle": "automatic"
Expand Down
22 changes: 16 additions & 6 deletions example/src/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import { SafeAreaView } from 'react-native-safe-area-context';

const isWeb = Platform.OS === 'web';

// list of countries that should be shown first in the country picker.
// Put this variable outside the component to avoid re-creating it on each render
// Or you can use useMemo() hook to create it.
// Put these variables outside the component to avoid re-creating it on each render
// Or you can use useMemo() hook to create them.
const countriesToShowFirst = ['BD', 'US', 'CA', 'GB', 'AU', 'IN', 'NZ'];
const countriesToInclude = ['AZ', 'BD', 'CA', 'GB', 'IN', 'NZ', 'US', 'TR', 'AU'];

const Application: React.FC = () => {
const [countryCode, setCountryCode] = useState<string>('BD'); // Default country code
Expand Down Expand Up @@ -46,7 +46,7 @@ const Application: React.FC = () => {
setPhoneNumber={setPhoneNumber}
showFirstOnList={countriesToShowFirst}
modalStyle={isWeb ? styles.web : undefined}
onlyCountries={['AZ', 'BD', 'CA', 'GB', 'IN', 'NZ', 'US', 'TR', 'AU']}
limitMaxLength
/>
<Surface elevation={5} style={styles.country}>
<View style={styles.left}>
Expand All @@ -67,11 +67,22 @@ const Application: React.FC = () => {
<Button onPress={() => ref.current?.closeCountryPicker()}>Close Picker</Button>
</View>
</Surface>
<Text style={styles.title} variant="titleLarge">
Only Show Specific Countries
</Text>
<PhoneNumberInput
code={countryCode}
setCode={setCountryCode}
phoneNumber={phoneNumber}
setPhoneNumber={setPhoneNumber}
showFirstOnList={countriesToShowFirst}
modalStyle={isWeb ? styles.web : undefined}
includeCountries={countriesToInclude}
/>
<Text style={styles.title} variant="titleLarge">
Disabled State
</Text>
<PhoneNumberInput
ref={ref}
code={countryCode}
setCode={setCountryCode}
phoneNumber={phoneNumber}
Expand All @@ -83,7 +94,6 @@ const Application: React.FC = () => {
Not Editable
</Text>
<PhoneNumberInput
ref={ref}
code={countryCode}
setCode={setCountryCode}
phoneNumber={phoneNumber}
Expand Down
48 changes: 34 additions & 14 deletions src/CountryPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const CountryPicker = forwardRef<CountryPickerRef, CountryPickerProps>(
showFirstOnList,
modalStyle,
modalContainerStyle,
includeCountries,
excludeCountries,
// Prpos from TextInput that needs special handling
disabled,
editable = true,
Expand Down Expand Up @@ -70,22 +72,40 @@ export const CountryPicker = forwardRef<CountryPickerRef, CountryPickerProps>(
}));

const countriesList = useMemo(() => {
if (!showFirstOnList?.length) {
return countries;
// By default, show all countries.
let filteredCountries = countries;

// First filter the countries based on the includeCountries.
if (Array.isArray(includeCountries) && includeCountries.length > 0) {
filteredCountries = includeCountries.map((code) => ({
...getCountryByCode(code),
code,
}));
}

const countriesToShowOnTop = showFirstOnList.map((code) => ({
...getCountryByCode(code),
code,
}));

return [
...countriesToShowOnTop,
...countries.filter(
(country) => !countriesToShowOnTop.some((c) => c.code === country.code)
),
];
}, [showFirstOnList]);
// If showFirstOnList is provided, show those countries on top of the list.
if (Array.isArray(showFirstOnList) && showFirstOnList.length > 0) {
// If the country is not in the includeCountries, do not show it.
// This is to prevent showing countries that are not in the includeCountries list.
const countriesToShowOnTop = filteredCountries.filter((country) =>
showFirstOnList.includes(country.code)
);

filteredCountries = countriesToShowOnTop.concat(
// Filter out the countries that are already shown on top.
filteredCountries.filter((country) => !showFirstOnList.includes(country.code))
);
}

// If excludeCountries is provided, filter out those countries.
if (Array.isArray(excludeCountries) && excludeCountries.length > 0) {
filteredCountries = filteredCountries.filter(
(country) => !excludeCountries.includes(country.code)
);
}

return filteredCountries;
}, [showFirstOnList, includeCountries, excludeCountries]);

const searchResult = useMemo(() => {
if (!debouncedSearchQuery) {
Expand Down
50 changes: 33 additions & 17 deletions src/PhoneNumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export const PhoneNumberInput = forwardRef<PhoneNumberInputRef, PhoneNumberInput
showFirstOnList,
modalStyle,
modalContainerStyle,
onlyCountries = [], // Add the new prop
includeCountries,
excludeCountries,
limitMaxLength,
// Props from TextInput that needs special handling
disabled,
editable = true,
Expand Down Expand Up @@ -78,27 +80,40 @@ export const PhoneNumberInput = forwardRef<PhoneNumberInputRef, PhoneNumberInput
}));

const countriesList = useMemo(() => {
// By default, show all countries.
let filteredCountries = countries;
if (onlyCountries.length > 0) {
filteredCountries = countries.filter((country) => onlyCountries.includes(country.code));

// First filter the countries based on the includeCountries.
if (Array.isArray(includeCountries) && includeCountries.length > 0) {
filteredCountries = includeCountries.map((code) => ({
...getCountryByCode(code),
code,
}));
}

if (!showFirstOnList?.length) {
return filteredCountries;
// If showFirstOnList is provided, show those countries on top of the list.
if (Array.isArray(showFirstOnList) && showFirstOnList.length > 0) {
// If the country is not in the includeCountries, do not show it.
// This is to prevent showing countries that are not in the includeCountries list.
const countriesToShowOnTop = filteredCountries.filter((country) =>
showFirstOnList.includes(country.code)
);

filteredCountries = countriesToShowOnTop.concat(
// Filter out the countries that are already shown on top.
filteredCountries.filter((country) => !showFirstOnList.includes(country.code))
);
}

const countriesToShowOnTop = showFirstOnList.map((code) => ({
...getCountryByCode(code),
code,
}));
// If excludeCountries is provided, filter out those countries.
if (Array.isArray(excludeCountries) && excludeCountries.length > 0) {
filteredCountries = filteredCountries.filter(
(country) => !excludeCountries.includes(country.code)
);
}

return [
...countriesToShowOnTop,
...filteredCountries.filter(
(country) => !countriesToShowOnTop.some((c) => c.code === country.code)
),
];
}, [showFirstOnList, onlyCountries]);
return filteredCountries;
}, [showFirstOnList, includeCountries, excludeCountries]);

const searchResult = useMemo(() => {
if (!debouncedSearchQuery) {
Expand Down Expand Up @@ -156,7 +171,7 @@ export const PhoneNumberInput = forwardRef<PhoneNumberInputRef, PhoneNumberInput
value={`${country.flag} ${country.dialCode} ${phoneNumber}`}
keyboardType={keyboardType || 'phone-pad'}
theme={themeWithFlagsFont}
maxLength={baselineLength + country.length}
maxLength={limitMaxLength ? baselineLength + country.length : undefined}
/>
<TouchableRipple
disabled={disabled || !editable}
Expand Down Expand Up @@ -214,6 +229,7 @@ export const PhoneNumberInput = forwardRef<PhoneNumberInputRef, PhoneNumberInput
onPress={() => {
setCode(item.code);
setVisible(false);
limitMaxLength && item.length < phoneNumber.length && setPhoneNumber('');
}}
theme={theme}
>
Expand Down
5 changes: 0 additions & 5 deletions src/data/countries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,3 @@ export const countriesMap: CountriesMap = {
ZM: { name: 'Zambia', flag: '🇿🇲', dialCode: '+260', length: 9 },
ZW: { name: 'Zimbabwe', flag: '🇿🇼', dialCode: '+263', length: 9 },
};

export const coentryLongestDialCodeLength = countries.reduce(
(longest, country) => Math.max(longest, country.dialCode.length),
0
);
7 changes: 5 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export interface PhoneNumberInputProps extends Omit<TextInputProps, 'value' | 'o
phoneNumber?: string;
setPhoneNumber: React.Dispatch<React.SetStateAction<string | undefined>>;
showFirstOnList?: string[];
onlyCountries? : string[];
includeCountries?: string[];
excludeCountries?: string[];
limitMaxLength?: boolean;
modalStyle?: StyleProp<ViewStyle>;
modalContainerStyle?: StyleProp<ViewStyle>;
}
Expand All @@ -32,7 +34,8 @@ export interface CountryPickerRef {
export interface CountryPickerProps extends Omit<TextInputProps, 'value' | 'onChangeText'> {
country?: string;
setCountry: React.Dispatch<React.SetStateAction<string>>;
onlyCountries? : string[];
includeCountries?: string[];
excludeCountries?: string[];
showFirstOnList?: string[];
modalStyle?: StyleProp<ViewStyle>;
modalContainerStyle?: StyleProp<ViewStyle>;
Expand Down

0 comments on commit 5d43ae3

Please sign in to comment.