react-native-country-select 0.3.2 → 0.3.3

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
@@ -330,42 +330,43 @@ export default function App() {
330
330
 
331
331
  ## CountrySelect Props ([countrySelectProps](https://github.com/AstrOOnauta/react-native-country-select/blob/main/lib/interface/countrySelectProps.ts))
332
332
 
333
- | Prop | Type | Required | Default | Description |
334
- | ---------------------------- | ----------------------------------------------------------------------- | -------- | -------------------- | ---------------------------------------------------------------- |
335
- | visible | boolean | Yes | false | Controls the visibility of the country picker modal |
336
- | onClose | () => void | Yes | - | Callback function called when the modal is closed |
337
- | onSelect | (country: [ICountry](lib/interfaces/country.ts)) => void | Yes | - | Callback function called when a country is selected |
338
- | modalType | 'bottomSheet' \| 'popup' | No | 'bottomSheet' | Type of modal to display |
339
- | countrySelectStyle | [ICountrySelectStyle](lib/interfaces/countrySelectStyles.ts) | No | - | Custom styles for the country picker |
340
- | isMultiSelect | boolean | No | false | Whether the user can select multiple options |
341
- | selectedCountries | [ICountry[]](lib/interfaces/country.ts) | No | - | Array of countries to show in multi select mode |
342
- | isFullScreen | boolean | No | false | Whether the modal should be full screen |
343
- | popularCountries | string[] | No | [] | Array of country codes to show in popular section |
344
- | visibleCountries | [ICountryCca2[]](lib/interfaces/countryCca2.ts) | No | [] | Array of country codes to show (whitelist) |
345
- | hiddenCountries | [ICountryCca2[]](lib/interfaces/countryCca2.ts) | No | [] | Array of country codes to hide (blacklist) |
346
- | theme | 'light' \| 'dark' | No | 'light' | Theme for the country picker |
347
- | language | [ICountrySelectLanguages](lib/interfaces/countrySelectLanguages.ts) | No | 'eng' | Language for country names (see supported languages below) |
348
- | showSearchInput | boolean | No | true | Whether to show the search input field |
349
- | showAlphabetFilter | boolean | No | false | Whether to show the alphabetic filter on modal |
350
- | searchPlaceholder | string | No | 'Search country...' | Placeholder text for search input |
351
- | searchPlaceholderTextColor | string | No | '#00000080' | Placeholder text color for search input |
352
- | searchSelectionColor | string | No | default | Highlight, selection handle and cursor color of the search input |
353
- | minBottomsheetHeight | number \| string | No | 30% | Minimum height for bottom sheet modal |
354
- | maxBottomsheetHeight | number \| string | No | 80% | Maximum height for bottom sheet modal |
355
- | initialBottomsheetHeight | number \| string | No | 50% | Initial height for bottom sheet modal |
356
- | disabledBackdropPress | boolean | No | false | Whether to disable backdrop press to close |
357
- | removedBackdrop | boolean | No | false | Whether to remove the backdrop completely |
358
- | onBackdropPress | () => void | No | - | Custom callback for backdrop press |
359
- | dragHandleIndicatorComponent | () => ReactElement | - | - | Custom component for drag handle indicator on bottom sheet |
360
- | countryItemComponent | (item: [ICountry](lib/interfaces/country.ts)) => ReactElement | No | - | Custom component for country items |
361
- | sectionTitleComponent | (item: [ISectionTitle](lib/interfaces/sectionTitle.ts)) => ReactElement | No | - | Custom component for section titles |
362
- | closeButtonComponent | () => ReactElement | No | - | Custom component for closeButton |
363
- | showCloseButton | boolean | No | false | Whether to show the close button |
364
- | popularCountriesTitle | string | No | 'Popular Countries' | Popular Countries section title |
365
- | allCountriesTitle | string | No | 'All Countries' | All Countries section title |
366
- | showsVerticalScrollIndicator | boolean | No | false | Displays a horizontal scroll indicator |
367
- | countryNotFoundMessage | string | No | "No countries found" | Country not found in search |
368
- | allowFontScaling | boolean | No | true | Whether to allow font scaling for text elements |
333
+ | Prop | Type | Required | Default | Description |
334
+ | ---------------------------- | ----------------------------------------------------------------------- | -------- | -------------------- | -------------------------------------------------------------------------------------- |
335
+ | visible | boolean | Yes | false | Controls the visibility of the country picker modal |
336
+ | onClose | () => void | Yes | - | Callback function called when the modal is closed |
337
+ | onSelect | (country: [ICountry](lib/interfaces/country.ts)) => void | Yes | - | Callback function called when a country is selected |
338
+ | modalType | 'bottomSheet' \| 'popup' | No | 'bottomSheet' | Type of modal to display |
339
+ | countrySelectStyle | [ICountrySelectStyle](lib/interfaces/countrySelectStyles.ts) | No | - | Custom styles for the country picker |
340
+ | isMultiSelect | boolean | No | false | Whether the user can select multiple options |
341
+ | selectedCountries | [ICountry[]](lib/interfaces/country.ts) | No | - | Array of countries to show in multi select mode |
342
+ | isFullScreen | boolean | No | false | Whether the modal should be full screen |
343
+ | popularCountries | string[] | No | [] | Array of country codes to show in popular section |
344
+ | visibleCountries | [ICountryCca2[]](lib/interfaces/countryCca2.ts) | No | [] | Array of country codes to show (whitelist) |
345
+ | hiddenCountries | [ICountryCca2[]](lib/interfaces/countryCca2.ts) | No | [] | Array of country codes to hide (blacklist) |
346
+ | theme | 'light' \| 'dark' | No | 'light' | Theme for the country picker |
347
+ | language | [ICountrySelectLanguages](lib/interfaces/countrySelectLanguages.ts) | No | 'eng' | Language for country names (see supported languages below) |
348
+ | showSearchInput | boolean | No | true | Whether to show the search input field |
349
+ | showAlphabetFilter | boolean | No | false | Whether to show the alphabetic filter on modal |
350
+ | searchPlaceholder | string | No | 'Search country...' | Placeholder text for search input |
351
+ | searchPlaceholderTextColor | string | No | '#00000080' | Placeholder text color for search input |
352
+ | searchSelectionColor | string | No | default | Highlight, selection handle and cursor color of the search input |
353
+ | minBottomsheetHeight | number \| string | No | 30% | Minimum height for bottom sheet modal |
354
+ | maxBottomsheetHeight | number \| string | No | 80% | Maximum height for bottom sheet modal |
355
+ | initialBottomsheetHeight | number \| string | No | 50% | Initial height for bottom sheet modal |
356
+ | disabledBackdropPress | boolean | No | false | Whether to disable backdrop press to close |
357
+ | removedBackdrop | boolean | No | false | Whether to remove the backdrop completely |
358
+ | onBackdropPress | () => void | No | - | Custom callback for backdrop press |
359
+ | dragHandleIndicatorComponent | () => ReactElement | - | - | Custom component for drag handle indicator on bottom sheet |
360
+ | countryItemComponent | (item: [ICountry](lib/interfaces/country.ts)) => ReactElement | No | - | Custom component for country items |
361
+ | sectionTitleComponent | (item: [ISectionTitle](lib/interfaces/sectionTitle.ts)) => ReactElement | No | - | Custom component for section titles |
362
+ | closeButtonComponent | () => ReactElement | No | - | Custom component for closeButton |
363
+ | customFlag | (country: [ICountry](lib/interfaces/country.ts)) => ReactElement | No | - | Custom render function for country flags. Returns rendered element in selected country |
364
+ | showCloseButton | boolean | No | false | Whether to show the close button |
365
+ | popularCountriesTitle | string | No | 'Popular Countries' | Popular Countries section title |
366
+ | allCountriesTitle | string | No | 'All Countries' | All Countries section title |
367
+ | showsVerticalScrollIndicator | boolean | No | false | Displays a horizontal scroll indicator |
368
+ | countryNotFoundMessage | string | No | "No countries found" | Country not found in search |
369
+ | allowFontScaling | boolean | No | true | Whether to allow font scaling for text elements |
369
370
 
370
371
  <br>
371
372
 
@@ -13,6 +13,7 @@ export const CountryItem = memo<ICountryItemProps>(
13
13
  theme = 'light',
14
14
  language = 'eng',
15
15
  countrySelectStyle,
16
+ customFlag,
16
17
  accessibilityLabel,
17
18
  accessibilityHint,
18
19
  allowFontScaling = true,
@@ -40,14 +41,25 @@ export const CountryItem = memo<ICountryItemProps>(
40
41
  ]}
41
42
  onPress={() => onSelect(country)}
42
43
  >
43
- <Text
44
- testID="countrySelectItemFlag"
45
- style={[styles.flag, countrySelectStyle?.flag]}
46
- allowFontScaling={allowFontScaling}
44
+ {customFlag &&
45
+ customFlag(country) !== undefined &&
46
+ customFlag(country) !== null ? (
47
+ customFlag(country)
48
+ ) : (
49
+ <Text
50
+ testID="countrySelectItemFlag"
51
+ style={[styles.flag, countrySelectStyle?.flag]}
52
+ allowFontScaling={allowFontScaling}
53
+ >
54
+ {country.flag || country.cca2}
55
+ </Text>
56
+ )}
57
+ <View
58
+ style={[
59
+ styles.countryInfo,
60
+ countrySelectStyle?.countryInfo,
61
+ ]}
47
62
  >
48
- {country.flag || country.cca2}
49
- </Text>
50
- <View style={[styles.countryInfo, countrySelectStyle?.countryInfo]}>
51
63
  <Text
52
64
  testID="countrySelectItemCallingCode"
53
65
  style={[
@@ -73,5 +85,5 @@ export const CountryItem = memo<ICountryItemProps>(
73
85
  </View>
74
86
  </TouchableOpacity>
75
87
  );
76
- },
88
+ }
77
89
  );
@@ -51,6 +51,7 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
51
51
  sectionTitleComponent,
52
52
  countryItemComponent,
53
53
  closeButtonComponent,
54
+ customFlag,
54
55
  popularCountriesTitle,
55
56
  allCountriesTitle,
56
57
  showsVerticalScrollIndicator = false,
@@ -73,7 +74,9 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
73
74
  ...props
74
75
  }) => {
75
76
  const [searchQuery, setSearchQuery] = useState('');
76
- const [activeLetter, setActiveLetter] = useState<string | null>(null);
77
+ const [activeLetter, setActiveLetter] = useState<string | null>(
78
+ null
79
+ );
77
80
 
78
81
  const flatListRef = useRef<FlatList<IListItem>>(null);
79
82
  const isProgrammaticScroll = useRef(false);
@@ -118,8 +121,9 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
118
121
  }, [countriesList]);
119
122
 
120
123
  const keyExtractor = useCallback(
121
- (item: IListItem) => ('isSection' in item ? item.title : item.cca2),
122
- [],
124
+ (item: IListItem) =>
125
+ 'isSection' in item ? item.title : item.cca2,
126
+ []
123
127
  );
124
128
 
125
129
  const handlePressLetter = useCallback(
@@ -132,7 +136,8 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
132
136
  for (let i = index; i < countriesList.length; i++) {
133
137
  const item = countriesList[i];
134
138
  if (!('isSection' in item)) {
135
- const name = (item as ICountry)?.translations[language]?.common || '';
139
+ const name =
140
+ (item as ICountry)?.translations[language]?.common || '';
136
141
  if (name) {
137
142
  computedLetter = name[0].toUpperCase();
138
143
  }
@@ -149,7 +154,7 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
149
154
  viewPosition: 0,
150
155
  });
151
156
  },
152
- [countriesList, language],
157
+ [countriesList, language]
153
158
  );
154
159
 
155
160
  const handleCloseModal = () => {
@@ -172,29 +177,38 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
172
177
 
173
178
  const isCountrySelected = useCallback(
174
179
  (cca2: string) => selectedCountryCodes.has(cca2),
175
- [selectedCountryCodes],
180
+ [selectedCountryCodes]
176
181
  );
177
182
 
178
183
  const handleSelectCountry = useCallback(
179
184
  (country: ICountry) => {
185
+ const countryWithCustomFlag = customFlag
186
+ ? {
187
+ ...country,
188
+ customFlag: customFlag(country),
189
+ }
190
+ : country;
191
+
180
192
  if (isMultiSelect) {
181
193
  if (isCountrySelected(country.cca2)) {
182
194
  (onSelect as (countries: ICountry[]) => void)(
183
- selectedCountries.filter(c => c.cca2 !== country.cca2),
195
+ selectedCountries.filter((c) => c.cca2 !== country.cca2)
184
196
  );
185
197
  return;
186
198
  }
187
199
  (onSelect as (countries: ICountry[]) => void)([
188
200
  ...selectedCountries,
189
- country,
201
+ countryWithCustomFlag,
190
202
  ]);
191
203
  return;
192
204
  }
193
205
 
194
- (onSelect as (country: ICountry) => void)(country);
206
+ (onSelect as (country: ICountry) => void)(
207
+ countryWithCustomFlag
208
+ );
195
209
  onClose();
196
210
  },
197
- [isMultiSelect, isCountrySelected, selectedCountries],
211
+ [isMultiSelect, isCountrySelected, selectedCountries, customFlag]
198
212
  );
199
213
 
200
214
  const renderCloseButton = () => {
@@ -245,7 +259,8 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
245
259
  const it = v.item;
246
260
  const idx = v.index ?? -1;
247
261
  if (!('isSection' in it) && idx >= allCountriesStartIndex) {
248
- const name = (it as ICountry)?.translations[language]?.common || '';
262
+ const name =
263
+ (it as ICountry)?.translations[language]?.common || '';
249
264
  if (name) {
250
265
  updated = name[0].toUpperCase();
251
266
  }
@@ -253,7 +268,7 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
253
268
  }
254
269
  }
255
270
  setActiveLetter(updated);
256
- },
271
+ }
257
272
  ).current;
258
273
 
259
274
  const renderFlatList = () => {
@@ -297,7 +312,9 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
297
312
  keyExtractor={keyExtractor}
298
313
  renderItem={renderItem}
299
314
  keyboardShouldPersistTaps="handled"
300
- showsVerticalScrollIndicator={showsVerticalScrollIndicator || false}
315
+ showsVerticalScrollIndicator={
316
+ showsVerticalScrollIndicator || false
317
+ }
301
318
  style={[styles.list, countrySelectStyle?.list]}
302
319
  onViewableItemsChanged={onViewableItemsChanged}
303
320
  onMomentumScrollEnd={() => {
@@ -309,7 +326,10 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
309
326
  }}
310
327
  onScrollToIndexFailed={({ index, averageItemLength }) => {
311
328
  // Simple recovery: estimate offset, then retry scrollToIndex after measurement
312
- const estimatedOffset = Math.max(0, (averageItemLength || 0) * index);
329
+ const estimatedOffset = Math.max(
330
+ 0,
331
+ (averageItemLength || 0) * index
332
+ );
313
333
  flatListRef.current?.scrollToOffset({
314
334
  offset: estimatedOffset,
315
335
  animated: false,
@@ -336,7 +356,10 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
336
356
  <Text
337
357
  testID="countrySelectSectionTitle"
338
358
  accessibilityRole="header"
339
- style={[styles.sectionTitle, countrySelectStyle?.sectionTitle]}
359
+ style={[
360
+ styles.sectionTitle,
361
+ countrySelectStyle?.sectionTitle,
362
+ ]}
340
363
  allowFontScaling={allowFontScaling}
341
364
  >
342
365
  {popularCountriesTitle && index === 0
@@ -353,7 +376,8 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
353
376
  }
354
377
 
355
378
  const countryItem = item as ICountry;
356
- const selected = isMultiSelect && isCountrySelected(countryItem.cca2);
379
+ const selected =
380
+ isMultiSelect && isCountrySelected(countryItem.cca2);
357
381
  return (
358
382
  <CountryItem
359
383
  country={countryItem}
@@ -362,6 +386,7 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
362
386
  theme={theme as IThemeProps}
363
387
  language={language}
364
388
  countrySelectStyle={countrySelectStyle}
389
+ customFlag={customFlag}
365
390
  accessibilityLabel={accessibilityLabelCountryItem}
366
391
  accessibilityHint={accessibilityHintCountryItem}
367
392
  allowFontScaling={allowFontScaling}
@@ -375,7 +400,7 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
375
400
  sectionTitleComponent,
376
401
  isMultiSelect,
377
402
  isCountrySelected,
378
- ],
403
+ ]
379
404
  );
380
405
 
381
406
  const renderAlphabetFilter = () => {
@@ -388,10 +413,18 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
388
413
  countries={countriesList}
389
414
  allCountriesStartIndex={allCountriesStartIndex}
390
415
  countrySelectStyle={countrySelectStyle}
391
- accessibilityLabelAlphabetFilter={accessibilityLabelAlphabetFilter}
392
- accessibilityHintAlphabetFilter={accessibilityHintAlphabetFilter}
393
- accessibilityLabelAlphabetLetter={accessibilityLabelAlphabetLetter}
394
- accessibilityHintAlphabetLetter={accessibilityHintAlphabetLetter}
416
+ accessibilityLabelAlphabetFilter={
417
+ accessibilityLabelAlphabetFilter
418
+ }
419
+ accessibilityHintAlphabetFilter={
420
+ accessibilityHintAlphabetFilter
421
+ }
422
+ accessibilityLabelAlphabetLetter={
423
+ accessibilityLabelAlphabetLetter
424
+ }
425
+ accessibilityHintAlphabetLetter={
426
+ accessibilityHintAlphabetLetter
427
+ }
395
428
  allowFontScaling={allowFontScaling}
396
429
  />
397
430
  );
@@ -400,7 +433,10 @@ export const CountrySelect: React.FC<ICountrySelectProps> = ({
400
433
  const HeaderModal =
401
434
  showSearchInput || showCloseButton ? (
402
435
  <View
403
- style={[styles.searchContainer, countrySelectStyle?.searchContainer]}
436
+ style={[
437
+ styles.searchContainer,
438
+ countrySelectStyle?.searchContainer,
439
+ ]}
404
440
  >
405
441
  {(showCloseButton || isFullScreen) && renderCloseButton()}
406
442
  {showSearchInput && renderSearchInput()}
package/lib/index.d.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  ICountrySelectProps,
8
8
  ICountrySelectStyle,
9
9
  ICountrySelectLanguages,
10
+ ISectionTitle,
10
11
  } from './interface';
11
12
 
12
13
  declare function getAllCountries(): ICountry[];
@@ -16,21 +17,25 @@ declare function getCountryByCca2(cca2: string): ICountry | undefined;
16
17
  declare function getCountryByCca3(cca3: string): ICountry | undefined;
17
18
 
18
19
  declare function getCountriesByCallingCode(
19
- callingCode: string,
20
+ callingCode: string
20
21
  ): ICountry[] | undefined;
21
22
 
22
23
  declare function getCountriesByName(
23
24
  name: string,
24
- language: ICountrySelectLanguages,
25
+ language: ICountrySelectLanguages
25
26
  ): ICountry[] | undefined;
26
27
 
27
- declare function getCountriesByRegion(region: string): ICountry[] | undefined;
28
+ declare function getCountriesByRegion(
29
+ region: string
30
+ ): ICountry[] | undefined;
28
31
 
29
32
  declare function getCountriesBySubregion(
30
- subregion: string,
33
+ subregion: string
31
34
  ): ICountry[] | undefined;
32
35
 
33
- declare function getContriesDependents(cca2: string): ICountry[] | undefined;
36
+ declare function getContriesDependents(
37
+ cca2: string
38
+ ): ICountry[] | undefined;
34
39
 
35
40
  declare function getCountriesIndependents(): ICountry[] | undefined;
36
41
 
@@ -45,6 +50,7 @@ export {
45
50
  ICountrySelectProps,
46
51
  ICountrySelectStyle,
47
52
  ICountrySelectLanguages,
53
+ ISectionTitle,
48
54
  getAllCountries,
49
55
  getCountryByCca2,
50
56
  getCountryByCca3,
package/lib/index.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import {CountrySelect} from './components';
1
+ import { CountrySelect } from './components';
2
2
  import {
3
3
  ICountry,
4
4
  ICountryCca2,
@@ -6,6 +6,7 @@ import {
6
6
  ICountrySelectProps,
7
7
  ICountrySelectStyle,
8
8
  ICountrySelectLanguages,
9
+ ISectionTitle,
9
10
  } from './interface';
10
11
  import {
11
12
  getAllCountries,
@@ -40,4 +41,5 @@ export type {
40
41
  ICountrySelectProps,
41
42
  ICountrySelectStyle,
42
43
  ICountrySelectLanguages,
44
+ ISectionTitle,
43
45
  };
@@ -1,4 +1,4 @@
1
- import {ICountryCca2} from './countryCca2';
1
+ import { ICountryCca2 } from './countryCca2';
2
2
 
3
3
  // Currency interface
4
4
  export interface ICountryCurrency {
@@ -107,5 +107,6 @@ export interface ICountry {
107
107
  borders: string[];
108
108
  area: number;
109
109
  flag: string;
110
+ customFlag?: React.ReactElement | null | undefined;
110
111
  demonyms: ICountryDemonyms;
111
112
  }
@@ -1,3 +1,4 @@
1
+ import * as React from 'react';
1
2
  import { ICountry } from './country';
2
3
  import { IThemeProps } from './theme';
3
4
  import { ICountrySelectStyle } from './countrySelectStyles';
@@ -10,6 +11,9 @@ export interface ICountryItemProps {
10
11
  onSelect: (country: ICountry) => void;
11
12
  language: ICountrySelectLanguages;
12
13
  countrySelectStyle?: ICountrySelectStyle;
14
+ customFlag?: (
15
+ country: ICountry
16
+ ) => React.ReactElement | null | undefined;
13
17
  accessibilityLabel?: string;
14
18
  accessibilityHint?: string;
15
19
  allowFontScaling?: boolean;
@@ -34,6 +34,9 @@ interface ICountrySelectBaseProps extends ModalProps, IThemeProps {
34
34
  countryItemComponent?: (item: ICountry) => React.ReactElement;
35
35
  sectionTitleComponent?: (item: ISectionTitle) => React.ReactElement;
36
36
  closeButtonComponent?: () => React.ReactElement;
37
+ customFlag?: (
38
+ country: ICountry
39
+ ) => React.ReactElement | null | undefined;
37
40
  popularCountriesTitle?: string;
38
41
  allCountriesTitle?: string;
39
42
  showsVerticalScrollIndicator?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-country-select",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "🌍 React Native country picker with flags, search, TypeScript, i18n, and offline support. Lightweight, customizable, and designed with a modern UI.",
5
5
  "main": "lib/index.tsx",
6
6
  "types": "lib/index.d.ts",