UNPKG

rn-phone-input-field

Version:

A React Native phone number input component built from scratch, featuring a text input for number entry, a custom dropdown for selecting country codes, and validation logic using regex or country-specific rules. It supports formatting, localization, and s

127 lines (124 loc) 7.71 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var react_1 = __importStar(require("react")); var react_native_1 = require("react-native"); var assets_1 = __importDefault(require("../assets/assets")); var constants_1 = __importDefault(require("../constants/constants")); var picker_style_1 = require("./styles/picker.style"); // Memoized country option component to prevent unnecessary re-renders var EachOption = (0, react_1.memo)(function (_a) { var onSelect = _a.onSelect, item = _a.item, index = _a.index, darkMode = _a.darkMode, closeModal = _a.closeModal; var styles = (0, picker_style_1.usePickerStyles)(darkMode); var borderStyle = (0, react_1.useMemo)(function () { return (0, picker_style_1.customBorder)(index, darkMode).border; }, [index, darkMode]); var handlePress = (0, react_1.useCallback)(function () { onSelect === null || onSelect === void 0 ? void 0 : onSelect(item); closeModal(); }, [onSelect, item, closeModal]); return (<react_native_1.TouchableOpacity activeOpacity={0.7} onPress={handlePress} style={[styles.eachContainer, borderStyle]}> <react_native_1.Text style={styles.eachTextContainer}> <react_native_1.Text style={styles.eachText}>{item === null || item === void 0 ? void 0 : item.icon}</react_native_1.Text> {' '} {item === null || item === void 0 ? void 0 : item.countryName} {' '}+{item === null || item === void 0 ? void 0 : item.callingCode} </react_native_1.Text> </react_native_1.TouchableOpacity>); }); EachOption.displayName = 'EachOption'; var Picker = (0, react_1.memo)(function (_a) { var onSelect = _a.onSelect, darkMode = _a.darkMode, closeModal = _a.closeModal, searchInputProps = _a.searchInputProps; // Memoize initial country list to prevent recreation var allCountries = (0, react_1.useMemo)(function () { return Object.values(constants_1.default); }, []); var _b = (0, react_1.useState)(allCountries), filteredCountries = _b[0], setFilteredCountries = _b[1]; var _c = (0, react_1.useState)(''), searchText = _c[0], setSearchText = _c[1]; var searchTimeoutRef = (0, react_1.useRef)(); // Get styles first var styles = (0, picker_style_1.usePickerStyles)(darkMode); // Memoize styles to prevent recreation on every render var memoizedStyles = (0, react_1.useMemo)(function () { return styles; }, [styles]); // Optimized search functionality with debouncing var handleSearch = (0, react_1.useCallback)(function (text) { setSearchText(text); if (searchTimeoutRef.current) { clearTimeout(searchTimeoutRef.current); } searchTimeoutRef.current = setTimeout(function () { if (!text.trim()) { setFilteredCountries(allCountries); return; } var searchTerm = text.toLowerCase().trim(); var filtered = allCountries.filter(function (country) { var _a, _b, _c; var countryName = ((_a = country.countryName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || ''; var callingCode = ((_b = country.callingCode) === null || _b === void 0 ? void 0 : _b.toString()) || ''; return (countryName.includes(searchTerm) || callingCode.includes(searchTerm) || ((_c = country.countryCode) === null || _c === void 0 ? void 0 : _c.toLowerCase().includes(searchTerm))); }); setFilteredCountries(filtered); }, 300); // 300ms debounce }, [allCountries]); // Memoized close icon to prevent recreation var closeIcon = (0, react_1.useMemo)(function () { return (<react_native_1.Image source={{ uri: darkMode ? assets_1.default.closeDarkIcon : assets_1.default.closeDefaultIcon, }} resizeMode="contain" height={12} width={12}/>); }, [darkMode]); // Memoized render item function for FlatList var renderCountryItem = (0, react_1.useCallback)(function (_a) { var item = _a.item, index = _a.index; return (<EachOption onSelect={onSelect} item={item} darkMode={darkMode} index={index} closeModal={closeModal}/>); }, [onSelect, darkMode, closeModal]); // Memoized key extractor var keyExtractor = (0, react_1.useCallback)(function (item) { return item.countryCode; }, []); // Memoized empty component for better UX var EmptyComponent = (0, react_1.useMemo)(function () { return (<react_native_1.View style={memoizedStyles.emptyContainer}> <react_native_1.Text style={memoizedStyles.emptyText}> No countries found for "{searchText}" </react_native_1.Text> </react_native_1.View>); }, [memoizedStyles.emptyContainer, memoizedStyles.emptyText, searchText]); // Memoized getItemLayout for better FlatList performance var getItemLayout = (0, react_1.useCallback)(function (data, index) { return ({ length: 60, offset: 60 * index, index: index, }); }, []); return (<react_native_1.View style={memoizedStyles.bgWhite}> <react_native_1.View style={memoizedStyles.flexRow}> <react_native_1.TouchableOpacity style={memoizedStyles.iconButton} onPress={closeModal} accessibilityLabel="Close country picker" accessibilityRole="button"> {closeIcon} </react_native_1.TouchableOpacity> <react_native_1.TextInput placeholder="Search Country" value={searchText} onChangeText={handleSearch} placeholderTextColor={darkMode ? '#FFFFFF' : '#000000'} style={memoizedStyles.searchInput} autoCorrect={false} autoCapitalize="none" returnKeyType="search" clearButtonMode="while-editing" accessibilityLabel="Search countries" maxLength={50} blurOnSubmit={false} {...searchInputProps}/> </react_native_1.View> <react_native_1.FlatList data={filteredCountries} keyboardShouldPersistTaps="always" showsVerticalScrollIndicator={false} automaticallyAdjustKeyboardInsets={true} keyExtractor={keyExtractor} renderItem={renderCountryItem} contentContainerStyle={memoizedStyles.flatListContainer} initialNumToRender={15} maxToRenderPerBatch={10} windowSize={10} removeClippedSubviews={true} getItemLayout={getItemLayout} updateCellsBatchingPeriod={50} maintainVisibleContentPosition={{ minIndexForVisible: 0, autoscrollToTopThreshold: 10, }} ListEmptyComponent={EmptyComponent}/> </react_native_1.View>); }); Picker.displayName = 'Picker'; exports.default = Picker;