@twotalltotems/react-native-otp-input
Advanced tools
Comparing version 1.0.22 to 1.0.23
193
index.js
import React, { Component } from 'react' | ||
import { View, TextInput, TouchableWithoutFeedback, Clipboard, Platform, Keyboard } from 'react-native' | ||
import { View, TextInput, TouchableWithoutFeedback, Clipboard, Keyboard } from 'react-native' | ||
import PropTypes from 'prop-types' | ||
import styles from './styles' | ||
import { isAutoFillSupported } from './helpers/device' | ||
const majorVersionIOS = parseInt(Platform.Version, 10); | ||
const isOTPSupported = (Platform.OS === 'ios' && majorVersionIOS >= 12) | ||
export default class OTPInputView extends Component { | ||
@@ -29,3 +27,3 @@ static propTypes = { | ||
state = { | ||
digits: [], | ||
digits: this.props.code.split("") || [], | ||
selectedIndex: 0, | ||
@@ -37,8 +35,9 @@ } | ||
componentDidMount() { | ||
const focusIndex = this.props.code.length ? this.props.code.length - 1 : 0 | ||
const { code, autoFocusOnLoad } = this.props | ||
const focusIndex = code.length ? code.length - 1 : 0 | ||
this.setState({ | ||
digits: this.props.code.split(""), | ||
digits: code.split(""), | ||
}, () => { | ||
if (focusIndex === 0 && this.props.autoFocusOnLoad) { | ||
this._focusField(focusIndex) | ||
if (focusIndex === 0 && autoFocusOnLoad) { | ||
this.focusField(focusIndex) | ||
} | ||
@@ -61,17 +60,17 @@ }) | ||
handleKeyboardDidHide = () => { | ||
this._blurAllFields() | ||
return false | ||
this.blurAllFields() | ||
} | ||
checkPinCodeFromClipBoard = () => { | ||
const { pinCount } = this.props | ||
Clipboard.getString().then(code => { | ||
if (this._hasCheckedCode && code.length === this.props.pinCount && (this._code !== code)) { | ||
if (this.hasCheckedClipBoard && code.length === pinCount && (this.clipBoardCode !== code)) { | ||
this.setState({ | ||
digits: code.split(""), | ||
}, () => { | ||
this._blurAllFields() | ||
this.blurAllFields() | ||
}) | ||
} | ||
this._code = code | ||
this._hasCheckedCode = true | ||
this.clipBoardCode = code | ||
this.hasCheckedClipBoard = true | ||
}).catch(e => { | ||
@@ -81,73 +80,10 @@ }) | ||
componentWillReceiveProps(nextProps) { | ||
if (nextProps.code !== this.state.digits) { | ||
this.setState({ | ||
digits: nextProps.code.split(""), | ||
}, () => { | ||
this._focusField(0) | ||
}) | ||
} | ||
} | ||
onChangeText = (index, text) => { | ||
const { onCodeFilled, pinCount } = this.props | ||
const { digits } = this.state | ||
let newdigits = digits.slice() | ||
render() { | ||
return ( | ||
<View | ||
style={this.props.style} | ||
> | ||
<TouchableWithoutFeedback | ||
style={{width: '100%', height: '100%'}} | ||
onPress={ () => { | ||
let filledPinCount = this.state.digits.filter((digit) => { return !!digit }).length | ||
this._focusField(Math.min(filledPinCount, this.props.pinCount - 1)) | ||
}} | ||
> | ||
<View | ||
style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', width: '100%', height: '100%' }} | ||
> | ||
{this._renderTextFields()} | ||
</View> | ||
</TouchableWithoutFeedback> | ||
</View> | ||
); | ||
} | ||
_renderOneInputField = (index) => { | ||
const {codeInputFieldStyle, codeInputHighlightStyle} = this.props | ||
const {defaultTextFieldStyle} = styles | ||
return ( | ||
<View pointerEvents="none" key={index + "view"}> | ||
<TextInput | ||
underlineColorAndroid='rgba(0,0,0,0)' | ||
style={this.state.selectedIndex === index ? [defaultTextFieldStyle, codeInputFieldStyle, codeInputHighlightStyle] : [defaultTextFieldStyle, codeInputFieldStyle]} | ||
ref={ref => { this.fields[index] = ref }} | ||
onChangeText={text => { | ||
this._onChangeText(index, text) | ||
}} | ||
onKeyPress={({ nativeEvent: { key } }) => { this._onKeyPress(index, key) }} | ||
value={this.state.digits[index]} | ||
keyboardType="number-pad" | ||
textContentType= {isOTPSupported ? "oneTimeCode" : "none"} | ||
key={index} | ||
selectionColor="#00000000" | ||
/> | ||
</View> | ||
) | ||
} | ||
_renderTextFields = () => { | ||
let array = new Array() | ||
for (i = 0; i<this.props.pinCount; i++) { | ||
array[i] = i | ||
} | ||
return array.map(this._renderOneInputField) | ||
} | ||
_onChangeText = (index, text) => { | ||
const {onCodeFilled} = this.props | ||
let newdigits = this.state.digits.slice() | ||
const oldTextLength = newdigits[index] ? newdigits[index].length : 0 | ||
const newTextLength = text.length | ||
if (newTextLength - oldTextLength === this.props.pinCount) { //User copy pasted text in. | ||
if (newTextLength - oldTextLength === pinCount) { // user pasted text in. | ||
newdigits = text.split("").slice(oldTextLength, newTextLength) | ||
@@ -172,9 +108,9 @@ this.setState( {digits: newdigits }) | ||
let result = newdigits.join("") | ||
if (result.length >= this.props.pinCount) { | ||
if (result.length >= pinCount) { | ||
onCodeFilled && onCodeFilled(result) | ||
this._focusField(this.props.pinCount - 1) | ||
this._blurAllFields() | ||
this.focusField(pinCount - 1) | ||
this.blurAllFields() | ||
} else { | ||
if (text.length > 0 && index < this.props.pinCount - 1) { | ||
this._focusField(index + 1) | ||
if (text.length > 0 && index < pinCount - 1) { | ||
this.focusField(index + 1) | ||
} | ||
@@ -184,7 +120,8 @@ } | ||
_onKeyPress = (index, key) => { | ||
onKeyPressTextInput = (index, key) => { | ||
const { digits } = this.state | ||
if(key === 'Backspace') { | ||
if (!this.state.digits[index] && index > 0) { | ||
this._onChangeText(index - 1, '') | ||
this._focusField(index - 1) | ||
if (!digits[index] && index > 0) { | ||
this.onChangeText(index - 1, '') | ||
this.focusField(index - 1) | ||
} | ||
@@ -194,13 +131,13 @@ } | ||
_focusField = (index) => { | ||
this.fields[index].focus() | ||
this.setState({ | ||
selectedIndex: index | ||
}) | ||
focusField = (index) => { | ||
if (index < this.fields.length) { | ||
this.fields[index].focus() | ||
this.setState({ | ||
selectedIndex: index | ||
}) | ||
} | ||
} | ||
_blurAllFields = () => { | ||
for (field of this.fields) { | ||
field.blur() | ||
} | ||
blurAllFields = () => { | ||
this.fields.forEach(field => field.blur()) | ||
this.setState({ | ||
@@ -210,2 +147,56 @@ selectedIndex: -1, | ||
} | ||
renderOneInputField = ( _ , index ) => { | ||
const { codeInputFieldStyle, codeInputHighlightStyle } = this.props | ||
const { defaultTextFieldStyle } = styles | ||
const { selectedIndex, digits } = this.state | ||
return ( | ||
<View pointerEvents="none" key={index + "view"}> | ||
<TextInput | ||
underlineColorAndroid='rgba(0,0,0,0)' | ||
style={selectedIndex === index ? [defaultTextFieldStyle, codeInputFieldStyle, codeInputHighlightStyle] : [defaultTextFieldStyle, codeInputFieldStyle]} | ||
ref={ref => { this.fields[index] = ref }} | ||
onChangeText={text => { | ||
this.onChangeText(index, text) | ||
}} | ||
onKeyPress={({ nativeEvent: { key } }) => { this.onKeyPressTextInput(index, key) }} | ||
value={digits[index]} | ||
keyboardType="number-pad" | ||
textContentType= {isAutoFillSupported ? "oneTimeCode" : "none"} | ||
key={index} | ||
selectionColor="#00000000" | ||
/> | ||
</View> | ||
) | ||
} | ||
renderTextFields = () => { | ||
const { pinCount } = this.props | ||
const array = new Array(pinCount).fill(0) | ||
return array.map(this.renderOneInputField) | ||
} | ||
render() { | ||
const { pinCount, style } = this.props | ||
const { digits } = this.state | ||
return ( | ||
<View | ||
style={style} | ||
> | ||
<TouchableWithoutFeedback | ||
style={{ width: '100%', height: '100%' }} | ||
onPress={() => { | ||
let filledPinCount = digits.filter((digit) => { return (digit !== null && digit !== undefined) }).length | ||
this.focusField(Math.min(filledPinCount, pinCount - 1)) | ||
}} | ||
> | ||
<View | ||
style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', width: '100%', height: '100%' }} | ||
> | ||
{this.renderTextFields()} | ||
</View> | ||
</TouchableWithoutFeedback> | ||
</View> | ||
); | ||
} | ||
} |
{ | ||
"name": "@twotalltotems/react-native-otp-input", | ||
"version": "1.0.22", | ||
"version": "1.0.23", | ||
"description": "is a tiny JS library for one time passcode (OTP). Supports smart input suggestion on iOS and code autofill on Android (it will be filled when you press the copy button on the SMS notification bar)", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -65,3 +65,3 @@  | ||
| pinCount | YES | Number of digits in the component | | ||
| code | NO | Besides providing an initial value, you can also enter this value using state or props. It will override the user input and reset the focus. For example, you can use it to hook up with the Android SMS Retriever API. | | ||
| code | NO | You can use this to override whatever user has typed. For example, you can use it to hook up with the Android SMS Retriever API. You should rarely need to use this one. But if you really need to, use it along with the key prop. Check our example app. | | ||
| codeInputFieldStyle | NO | The style of the input field which is NOT focused | | ||
@@ -68,0 +68,0 @@ | codeInputHighlightStyle | NO | The style of the input field which is focused | |
17610
9
188