UNPKG

@artmajeur/react-native-paper-phone-number-input

Version:

A performant phone number input component for react-native-paper with country picker

230 lines (226 loc) 7.25 kB
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { FlatList, Platform, PlatformColor, StyleSheet, View } from 'react-native'; import { DataTable, IconButton, Modal, Portal, Text, TextInput, TouchableRipple } from 'react-native-paper'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { isIOS } from './constants'; import { countries } from './data/countries'; import translatedCountries from './data/translatedCountries'; import { useCountriesList, useCountrySearch } from './hooks'; import { useDebouncedValue } from './use-debounced-value'; import useThemeWithFlagsFont from './useThemeWithFlagsFont'; export const CountryPicker = /*#__PURE__*/forwardRef(({ country, setCountry, showFirstOnList, modalStyle, modalContainerStyle, includeCountries, excludeCountries, // Prpos from TextInput that needs special handling disabled, editable = true, theme, lang = 'fr', placeholder = '', searchLabel = '', // rest of the props ...rest }, ref) => { const insets = useSafeAreaInsets(); const themeWithFlagsFont = useThemeWithFlagsFont(theme); // States for the modal const [visible, setVisible] = useState(false); const countryFlag = useMemo(() => { if (country) { const matchedCountry = countries.find(c => c.code.toLocaleLowerCase() === country.toLocaleLowerCase()); return matchedCountry?.flag; } return undefined; }, [country]); // States for the searchbar const [searchQuery, setSearchQuery] = useState(''); const debouncedSearchQuery = useDebouncedValue(searchQuery, 300); const searchbarRef = useRef(null); const openModal = useCallback(() => { setVisible(true); }, []); const closeModal = useCallback(() => { setVisible(false); }, []); // Focus the search bar when the modal becomes visible useEffect(() => { if (visible) { // We need a small delay to ensure the modal is fully animated and the search bar is rendered setTimeout(() => { searchbarRef.current?.focus(); }, 100); } }, [visible]); useImperativeHandle(ref, () => ({ openCountryPicker: openModal, closeCountryPicker: closeModal }), [openModal, closeModal]); const countriesList = useCountriesList({ showFirstOnList, includeCountries, excludeCountries }); const searchResult = useCountrySearch({ searchQuery: debouncedSearchQuery, countriesList, lang }); const handleCountrySelect = useCallback(selectedCountry => { setCountry(selectedCountry.code); closeModal(); }, [setCountry, closeModal]); const renderCountryItem = useCallback(({ item }) => /*#__PURE__*/React.createElement(DataTable.Row, { onPress: () => handleCountrySelect(item), theme: theme }, /*#__PURE__*/React.createElement(DataTable.Cell, { theme: themeWithFlagsFont }, `${item.flag} ${translatedCountries.getName(item.code, lang) || item.name}`)), [handleCountrySelect, theme, themeWithFlagsFont, lang]); const keyExtractor = useCallback(item => item.code, []); const value = useMemo(() => { if (country && countryFlag) { return `${countryFlag} ${translatedCountries.getName(country, lang)}`; } return placeholder; }, [country, countryFlag, lang, placeholder]); // Dynamic styles based on theme const dynamicStyles = useMemo(() => ({ searchbar: { flex: 1 }, searchbarContent: { backgroundColor: 'transparent', fontSize: 16 }, outlined: { borderRadius: 30, borderColor: theme?.dark ? '#343740' : '#CBD5E1' } }), [theme]); return /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(TextInput, _extends({ right: /*#__PURE__*/React.createElement(TextInput.Icon, { icon: "chevron-down" }) }, rest, { disabled: disabled, editable: editable, value: value, theme: themeWithFlagsFont })), /*#__PURE__*/React.createElement(TouchableRipple, { disabled: disabled || !editable, style: styles.ripple, onPress: openModal, theme: theme }, /*#__PURE__*/React.createElement(Text, null, " ")), /*#__PURE__*/React.createElement(Portal, { theme: theme }, /*#__PURE__*/React.createElement(Modal, { style: [styles.modal, modalStyle], contentContainerStyle: [styles.countries, { backgroundColor: themeWithFlagsFont.colors.background, paddingTop: insets.top + 16, paddingBottom: insets.bottom + 16 },, modalContainerStyle], visible: visible, onDismiss: closeModal, theme: theme }, /*#__PURE__*/React.createElement(View, { style: styles.searchbox }, /*#__PURE__*/React.createElement(IconButton, { icon: "arrow-left", onPress: closeModal, theme: theme }), /*#__PURE__*/React.createElement(TextInput, { style: [styles.searchbar, dynamicStyles.searchbar], placeholder: searchLabel, onChangeText: setSearchQuery, value: searchQuery, ref: searchbarRef, mode: "outlined", dense: true, theme: theme, onKeyPress: ({ nativeEvent }) => { if (nativeEvent.key === 'Escape') { closeModal(); } }, selectionColor: Platform.select({ ios: PlatformColor('systemBlue'), android: PlatformColor('@android:color/holo_blue_light') }), cursorColor: Platform.select({ android: PlatformColor('@android:color/holo_blue_light') }), left: /*#__PURE__*/React.createElement(TextInput.Icon, { icon: "magnify", size: 20, style: styles.searchIcon }), underlineStyle: styles.searchbarUnderline, contentStyle: [styles.searchbarContent, dynamicStyles.searchbarContent], outlineStyle: [styles.outlined, dynamicStyles.outlined] })), /*#__PURE__*/React.createElement(DataTable, { style: styles.flex1 }, /*#__PURE__*/React.createElement(FlatList, { keyboardShouldPersistTaps: "handled", data: searchResult, keyExtractor: keyExtractor, renderItem: renderCountryItem, removeClippedSubviews: true, maxToRenderPerBatch: 20, updateCellsBatchingPeriod: 50, initialNumToRender: 15, windowSize: 10 }))))); }); const styles = StyleSheet.create({ ripple: { position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }, flex1: { flex: isIOS ? undefined : 1 }, modal: { marginTop: undefined, marginBottom: undefined, justifyContent: undefined }, countries: { paddingHorizontal: 16, flex: isIOS ? undefined : 1, marginBottom: isIOS ? 150 : undefined, justifyContent: undefined }, searchbox: { flexDirection: 'row' }, searchbar: { flex: 1 }, searchbarContent: { backgroundColor: 'transparent' }, searchbarUnderline: { display: 'none' }, outlined: { borderRadius: 30 }, searchIcon: { alignSelf: 'center', marginTop: 15 } }); //# sourceMappingURL=CountryPicker.js.map