UNPKG

@pakenfit/react-native-pin-input

Version:
154 lines 5.52 kB
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react'; import { StyleSheet } from 'react-native'; import { View, Keyboard } from 'react-native'; import { Input } from './Input'; import * as Clipboard from 'expo-clipboard'; import { IS_IOS } from '../constants'; export const PinInput = /*#__PURE__*/forwardRef((_ref, ref) => { let { length = 4, inputProps, inputStyle, containerProps, containerStyle, onFillEnded, autoFocus = true } = _ref; const pins = Array.from({ length }).map((_, i) => i); const inputRefs = useRef([]); const pinsValues = useRef([]); const iosOTP = useRef({ key: '', index: null }); const [keyPressed, setKeyPressed] = useState(false); const handleOTP = useCallback(otp => { const regexp = new RegExp(`[0-9]{${length}}`); const otps = otp.match(regexp); if (otps !== null && otps !== void 0 && otps.length) { const otpSplits = otp.split(''); otpSplits.forEach((otpSplit, i) => { var _inputRefs$current$i; return inputRefs === null || inputRefs === void 0 || (_inputRefs$current$i = inputRefs.current[i]) === null || _inputRefs$current$i === void 0 ? void 0 : _inputRefs$current$i.setNativeProps({ text: otpSplit }); }); onFillEnded === null || onFillEnded === void 0 || onFillEnded(otp); iosOTP.current = { key: '', index: null }; Keyboard.dismiss(); return true; } return false; }, [length, onFillEnded]); const handleChangeText = useCallback(async (text, index) => { const copiedText = await Clipboard.getStringAsync(); if (copiedText.includes(text) && !keyPressed) { const otpHandled = handleOTP(copiedText); if (otpHandled) { return; } } pinsValues.current[index] = text; if (index + 1 <= pins.length - 1) { var _inputRefs$current; inputRefs === null || inputRefs === void 0 || (_inputRefs$current = inputRefs.current[index + 1]) === null || _inputRefs$current === void 0 || _inputRefs$current.focus(); } else { onFillEnded === null || onFillEnded === void 0 || onFillEnded(pinsValues.current.join('')); setKeyPressed(false); Keyboard.dismiss(); } }, [handleOTP, keyPressed, onFillEnded, pins.length]); const onKeyPress = useCallback((event, index) => { event.persist(); setKeyPressed(true); if (IS_IOS && Number.isInteger(Number(event.nativeEvent.key))) { if (iosOTP.current.index === null) { iosOTP.current = { key: event.nativeEvent.key, index }; } else { if (iosOTP.current.index === index) { iosOTP.current = { key: `${iosOTP.current.key}${event.nativeEvent.key}`, index }; } else { iosOTP.current = { key: '', index: null }; } } if (iosOTP.current.key.length === length) { handleOTP(iosOTP.current.key); return; } } if (event.nativeEvent.key === 'Backspace') { // Clear only the current digit if it has value if (pinsValues.current[index]) { pinsValues.current[index] = ''; // Don't move focus - stay on current field // We only reset partial state, not the entire PIN } else { // Only move to previous field when current field is empty if (index - 1 >= 0) { var _inputRefs$current2; inputRefs === null || inputRefs === void 0 || (_inputRefs$current2 = inputRefs.current[index - 1]) === null || _inputRefs$current2 === void 0 || _inputRefs$current2.focus(); } } setKeyPressed(false); iosOTP.current = { key: '', index: null }; } }, [handleOTP, length]); const clear = useCallback(() => { var _inputRefs$current$; pinsValues.current = []; inputRefs.current.forEach(input => { input === null || input === void 0 || input.setNativeProps({ text: '', placeholder: '0' }); }); (_inputRefs$current$ = inputRefs.current[0]) === null || _inputRefs$current$ === void 0 || _inputRefs$current$.focus(); }, []); useImperativeHandle(ref, () => ({ clear }), [clear]); return /*#__PURE__*/React.createElement(View, _extends({ style: [styles.container, containerStyle] }, containerProps), pins.map(pin => { return /*#__PURE__*/React.createElement(Input, _extends({}, inputProps, { autoFocus: autoFocus && pin === 0, ref: input => inputRefs === null || inputRefs === void 0 ? void 0 : inputRefs.current.push(input), key: pin, style: inputStyle, onChangeText: text => handleChangeText(text, pin), onKeyPress: event => onKeyPress(event, pin), autoComplete: "sms-otp", textContentType: "oneTimeCode", keyboardType: "numeric" })); })); }); PinInput.displayName = 'PinInput'; const styles = StyleSheet.create({ container: { display: 'flex', flexDirection: 'row', gap: 5, alignItems: 'center', justifyContent: 'center' } }); //# sourceMappingURL=PinInput.js.map