Skip to content

Commit 98f2e57

Browse files
committed
chore: removed withAlphaFilter, fixed filters, many other improvements
* the alpha scroll-to-a-letter thing works even in languages with many characters, with preferred countries, and with Åland * search works with modern fuse, and will index against alternative names for the country * buttons invalidate correctly when country code is changed (name and calling code were not updated) * flag button still shows something even if all the button content options are off * if search has no hits, text turns red and full list displays * the side scroller no longer filters letters, it just jumps to list positions. the withAlphaFilter option has been removed, but will still toggle on/off the side scroller index if it is not explicitly set on or off. * some async error handling
1 parent 9faac6d commit 98f2e57

File tree

12 files changed

+286
-245
lines changed

12 files changed

+286
-245
lines changed

App.tsx

Lines changed: 136 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,28 @@
11
import React, { useState } from 'react'
2-
import {
3-
Text,
4-
StyleSheet,
5-
PixelRatio,
6-
Switch,
7-
Button,
8-
ScrollView,
9-
} from 'react-native'
10-
import CountryPicker, { CountryModalProvider } from './src/'
2+
import { Text, StyleSheet, PixelRatio, Switch, Button, Pressable, ScrollView, TextInput, View } from 'react-native'
3+
import CountryPicker, { CountryModalProvider, TranslationLanguageCodeList } from './src/'
114
import { CountryCode, Country } from './src/types'
125
import { Row } from './src/Row'
136
import { DARK_THEME } from './src/CountryTheme'
147

158
const styles = StyleSheet.create({
16-
container: {
17-
paddingVertical: 10,
18-
justifyContent: 'center',
19-
alignItems: 'center',
20-
},
21-
welcome: {
22-
fontSize: 17,
23-
textAlign: 'center',
24-
margin: 5,
25-
},
26-
instructions: {
27-
fontSize: 10,
28-
textAlign: 'center',
29-
color: '#888',
30-
marginBottom: 0,
31-
},
32-
data: {
33-
maxWidth: 250,
34-
padding: 10,
35-
marginTop: 7,
36-
backgroundColor: '#ddd',
37-
borderColor: '#888',
38-
borderWidth: 1 / PixelRatio.get(),
39-
color: '#777',
40-
},
9+
mainBox: { maxHeight: 2000, paddingVertical: 10, justifyContent: 'space-between', alignItems: 'center', flex: 1, },
10+
container: { paddingVertical: 10, justifyContent: 'space-between', alignItems: 'center', },
11+
button: { paddingVertical: 5, paddingHorizontal: 10, marginHorizontal: 4, backgroundColor: '#cfd4e7' },
12+
countryPickerBox: { maxHeight: '30%', paddingVertical: 5, paddingHorizontal: 10, backgroundColor: '#eee' },
13+
optionsBox: { maxHeight: '30%', marginVertical: 10, borderColor: '#abc', borderWidth: 1 / PixelRatio.get() },
14+
dataBox: { maxHeight: '25%', flex: 1, width: '90%', marginHorizontal: 5, borderWidth: 1 / PixelRatio.get() },
15+
textEntryBox: { flex: 1, padding: 5, marginLeft: 20, flexDirection: 'row', justifyContent: 'flex-start' },
16+
buttonsBox: { flexDirection: 'row', marginTop: 2 },
17+
//
18+
welcome: { fontSize: 17, textAlign: 'center', marginHorizontal: 5, marginVertical: 10, },
19+
heading: { fontSize: 17, textAlign: 'center', marginHorizontal: 5, marginVertical: 10, },
20+
instructions: { fontSize: 10, textAlign: 'center', color: '#888', marginBottom: 5, paddingHorizontal: 15, },
21+
data: { padding: 10, marginTop: 7, backgroundColor: '#ddd', borderColor: '#888', borderWidth: 1 / PixelRatio.get(), color: '#777' },
22+
//
23+
textEntryLabel: { flex: 2, width: '25%' },
24+
textEntryInput: { flex: 3, marginLeft: 2 },
25+
//
4126
})
4227

4328
interface OptionProps {
@@ -55,137 +40,143 @@ const Option = ({ value, onValueChange, title }: OptionProps) => (
5540
export default function App() {
5641
const [countryCode, setCountryCode] = useState<CountryCode | undefined>()
5742
const [country, setCountry] = useState<Country>(null)
58-
const [withCountryNameButton, setWithCountryNameButton] = useState<boolean>(
59-
false,
60-
)
43+
//
44+
const [withCountryNameButton, setWithCountryNameButton] = useState<boolean>(false)
6145
const [withCurrencyButton, setWithCurrencyButton] = useState<boolean>(false)
6246
const [withFlagButton, setWithFlagButton] = useState<boolean>(true)
63-
const [withCallingCodeButton, setWithCallingCodeButton] = useState<boolean>(
64-
false,
65-
)
47+
const [withCallingCodeButton, setWithCallingCodeButton] = useState<boolean>(false)
6648
const [withFlag, setWithFlag] = useState<boolean>(true)
6749
const [withEmoji, setWithEmoji] = useState<boolean>(true)
6850
const [withFilter, setWithFilter] = useState<boolean>(true)
69-
const [withAlphaFilter, setWithAlphaFilter] = useState<boolean>(false)
51+
const [withLetterScroller, setWithLetterScroller] = useState<boolean>(true)
7052
const [withCallingCode, setWithCallingCode] = useState<boolean>(false)
7153
const [withCurrency, setWithCurrency] = useState<boolean>(false)
72-
const [withModal, setWithModal] = useState<boolean>(true)
54+
const [withModal, setWithModal] = useState<boolean>(false)
7355
const [visible, setVisible] = useState<boolean>(false)
56+
const [withDependents, setWithDependents] = useState<boolean>(false)
7457
const [dark, setDark] = useState<boolean>(false)
7558
const [fontScaling, setFontScaling] = useState<boolean>(true)
7659
const [disableNativeModal, setDisableNativeModal] = useState<boolean>(false)
60+
61+
const [preferredCountriesStr, setPreferredCountriesStr] = useState<string>('UA')
62+
const [excludeCountriesStr, setExcludeCountriesStr] = useState<string>('RU')
63+
const [translation, setTranslation] = useState<string>(null)
64+
65+
const resetCountry = () => {
66+
setCountry(null)
67+
setCountryCode(null)
68+
}
7769
const onSelect = (country: Country) => {
7870
setCountryCode(country.cca2)
7971
setCountry(country)
8072
}
8173
const switchVisible = () => setVisible(!visible)
8274

75+
const excludeCountries = excludeCountriesStr.toUpperCase().trim().split(/\W+/)
76+
const preferredCountries = preferredCountriesStr.toUpperCase().trim().split(/\W+/)
77+
8378
return (
8479
<CountryModalProvider>
85-
<ScrollView contentContainerStyle={styles.container}>
80+
<View style={styles.mainBox}>
8681
<Text style={styles.welcome}>Welcome to Country Picker !</Text>
87-
<Option
88-
title='With country name on button'
89-
value={withCountryNameButton}
90-
onValueChange={setWithCountryNameButton}
91-
/>
92-
<Option
93-
title='With currency on button'
94-
value={withCurrencyButton}
95-
onValueChange={setWithCurrencyButton}
96-
/>
97-
<Option
98-
title='With calling code on button'
99-
value={withCallingCodeButton}
100-
onValueChange={setWithCallingCodeButton}
101-
/>
102-
<Option
103-
title='With flag'
104-
value={withFlag}
105-
onValueChange={setWithFlag}
106-
/>
107-
<Option
108-
title='With font scaling'
109-
value={fontScaling}
110-
onValueChange={setFontScaling}
111-
/>
112-
<Option
113-
title='With emoji'
114-
value={withEmoji}
115-
onValueChange={setWithEmoji}
116-
/>
117-
<Option
118-
title='With filter'
119-
value={withFilter}
120-
onValueChange={setWithFilter}
121-
/>
122-
<Option
123-
title='With calling code'
124-
value={withCallingCode}
125-
onValueChange={setWithCallingCode}
126-
/>
127-
<Option
128-
title='With currency'
129-
value={withCurrency}
130-
onValueChange={setWithCurrency}
131-
/>
132-
<Option
133-
title='With alpha filter code'
134-
value={withAlphaFilter}
135-
onValueChange={setWithAlphaFilter}
136-
/>
137-
<Option
138-
title='Without native modal'
139-
value={disableNativeModal}
140-
onValueChange={setDisableNativeModal}
141-
/>
142-
<Option
143-
title='With modal'
144-
value={withModal}
145-
onValueChange={setWithModal}
146-
/>
147-
<Option title='With dark theme' value={dark} onValueChange={setDark} />
148-
<Option
149-
title='With flag button'
150-
value={withFlagButton}
151-
onValueChange={setWithFlagButton}
152-
/>
153-
<CountryPicker
154-
theme={dark ? DARK_THEME : {}}
155-
{...{
156-
allowFontScaling: fontScaling,
157-
countryCode,
158-
withFilter,
159-
excludeCountries: ['FR'],
160-
withFlag,
161-
withCurrencyButton,
162-
withCallingCodeButton,
163-
withCountryNameButton,
164-
withAlphaFilter,
165-
withCallingCode,
166-
withCurrency,
167-
withEmoji,
168-
withModal,
169-
withFlagButton,
170-
onSelect,
171-
disableNativeModal,
172-
preferredCountries: ['US', 'GB'],
173-
modalProps: {
174-
visible,
175-
},
176-
onClose: () => setVisible(false),
177-
onOpen: () => setVisible(true),
178-
}}
179-
/>
180-
<Text style={styles.instructions}>Press on the flag to open modal</Text>
181-
<Button
182-
title={'Open modal from outside using visible props'}
183-
onPress={switchVisible}
184-
/>
185-
{country !== null && (
186-
<Text style={styles.data}>{JSON.stringify(country, null, 0)}</Text>
187-
)}
188-
</ScrollView>
82+
<Text style={styles.instructions}>Press on the flag/placeholder to open modal:</Text>
83+
84+
<View style={styles.countryPickerBox}>
85+
<CountryPicker
86+
theme={dark ? DARK_THEME : {}}
87+
{...{
88+
allowFontScaling: fontScaling,
89+
countryCode,
90+
withFilter,
91+
excludeCountries,
92+
withFlag,
93+
withCurrencyButton,
94+
withCallingCodeButton,
95+
withCountryNameButton,
96+
withLetterScroller,
97+
withCallingCode,
98+
withCurrency,
99+
withEmoji,
100+
withModal,
101+
withFlagButton,
102+
withDependents,
103+
onSelect,
104+
disableNativeModal,
105+
preferredCountries,
106+
translation,
107+
modalProps: {
108+
visible,
109+
},
110+
onClose: () => setVisible(false),
111+
onOpen: () => setVisible(true),
112+
placeholder: '(-country-)',
113+
}}
114+
/>
115+
</View>
116+
117+
<View style={styles.optionsBox}>
118+
<ScrollView>
119+
<Text style={styles.heading}>Options:</Text>
120+
<View style={styles.textEntryBox}>
121+
<Text style={styles.textEntryLabel}>Preferred Countries: </Text>
122+
<TextInput
123+
style={styles.textEntryInput}
124+
value={preferredCountriesStr}
125+
onChangeText={setPreferredCountriesStr}
126+
autoCapitalize='characters'
127+
/>
128+
</View>
129+
<View style={styles.textEntryBox}>
130+
<Text style={styles.textEntryLabel}>Exclude Countries: </Text>
131+
<TextInput
132+
style={styles.textEntryInput}
133+
value={excludeCountriesStr}
134+
onChangeText={setExcludeCountriesStr}
135+
autoCapitalize='characters'
136+
/>
137+
</View>
138+
<View style={styles.textEntryBox}>
139+
<Text style={styles.textEntryLabel}>Translation:</Text>
140+
<TextInput style={styles.textEntryInput} autoCorrect={false} value={translation} onChangeText={setTranslation} autoCapitalize='none' />
141+
</View>
142+
<Text style={styles.instructions}>{TranslationLanguageCodeList.join(' / ')}</Text>
143+
<Text style={styles.instructions}>
144+
ex: {excludeCountries.join('|')} pr: {preferredCountries.join('|')}
145+
</Text>
146+
<Option title='Show country name on button' value={withCountryNameButton} onValueChange={setWithCountryNameButton} />
147+
<Option title='Show currency on button' value={withCurrencyButton} onValueChange={setWithCurrencyButton} />
148+
<Option title='Show calling code on button' value={withCallingCodeButton} onValueChange={setWithCallingCodeButton} />
149+
<Option title='Show flag on button' value={withFlagButton} onValueChange={setWithFlagButton} />
150+
<Option title='With font scaling' value={fontScaling} onValueChange={setFontScaling} />
151+
<Option title='Use emoji (not image) flags' value={withEmoji} onValueChange={setWithEmoji} />
152+
<Option title='Provide type-to-filter entry' value={withFilter} onValueChange={setWithFilter} />
153+
<Option title='Show flag in picker' value={withFlag} onValueChange={setWithFlag} />
154+
<Option title='Show calling code in picker' value={withCallingCode} onValueChange={setWithCallingCode} />
155+
<Option title='Show currency in picker' value={withCurrency} onValueChange={setWithCurrency} />
156+
<Option title='Show letter scroller on right' value={withLetterScroller} onValueChange={setWithLetterScroller} />
157+
<Option title='Include non-independent states' value={withDependents} onValueChange={setWithDependents} />
158+
<Option title='Without native modal' value={disableNativeModal} onValueChange={setDisableNativeModal} />
159+
<Option title='With modal' value={withModal} onValueChange={setWithModal} />
160+
<Option title='With dark theme' value={dark} onValueChange={setDark} />
161+
</ScrollView>
162+
</View>
163+
164+
<View style={styles.dataBox}>
165+
<ScrollView>
166+
<Text style={styles.heading}>Result:</Text>
167+
<Text style={styles.data}>{JSON.stringify(country, null, 2)}</Text>
168+
</ScrollView>
169+
</View>
170+
171+
<View style={styles.buttonsBox}>
172+
<Pressable onPress={resetCountry} style={styles.button}>
173+
<Text>Reset Country</Text>
174+
</Pressable>
175+
<Pressable onPress={switchVisible} style={styles.button}>
176+
<Text>Open Modal Picker</Text>
177+
</Pressable>
178+
</View>
179+
</View>
189180
</CountryModalProvider>
190181
)
191182
}

app.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Expo Country Picker Example",
55
"slug": "country-picker-example",
66
"privacy": "public",
7-
"sdkVersion": "35.0.0",
7+
"sdkVersion": "46.0.0",
88
"platforms": ["ios", "android", "web"],
99
"version": "1.0.0",
1010
"orientation": "portrait",

src/CountryContext.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
getCountryNameAsync,
77
getCountriesAsync,
88
getLetters,
9+
getScrollerLetter,
910
getCountryCallingCodeAsync,
1011
getCountryCurrencyAsync,
1112
getCountryInfoAsync,
@@ -19,6 +20,7 @@ export interface CountryContextParam {
1920
getEmojiFlagAsync: typeof getEmojiFlagAsync
2021
getCountriesAsync: typeof getCountriesAsync
2122
getLetters: typeof getLetters
23+
getScrollerLetter: typeof getScrollerLetter
2224
getCountryCallingCodeAsync: typeof getCountryCallingCodeAsync
2325
getCountryCurrencyAsync: typeof getCountryCurrencyAsync
2426
search: typeof search
@@ -34,6 +36,7 @@ export const DEFAULT_COUNTRY_CONTEXT = {
3436
getCountryCurrencyAsync,
3537
search,
3638
getLetters,
39+
getScrollerLetter,
3740
getCountryInfoAsync,
3841
}
3942
export const CountryContext = React.createContext<CountryContextParam>(

src/CountryFilter.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@ const styles = StyleSheet.create({
1414
},
1515
}),
1616
},
17+
thwarted: {
18+
color: 'red',
19+
},
1720
})
1821

19-
export type CountryFilterProps = TextInputProps
22+
export interface CountryFilterProps extends TextInputProps {
23+
thwarted?: boolean
24+
}
2025

21-
export const CountryFilter = (props: CountryFilterProps) => {
26+
export const CountryFilter = ({ thwarted, ...props }: CountryFilterProps) => {
2227
const {
2328
filterPlaceholderTextColor,
2429
fontFamily,
@@ -33,6 +38,7 @@ export const CountryFilter = (props: CountryFilterProps) => {
3338
style={[
3439
styles.input,
3540
{ fontFamily, fontSize, color: onBackgroundTextColor },
41+
thwarted && styles.thwarted,
3642
]}
3743
{...props}
3844
/>

0 commit comments

Comments
 (0)