1- import React , { Component } from 'react' ;
1+ import React , { Component } from 'react' ;
22import PropTypes from 'prop-types' ;
3- import { View , TextInput , StyleSheet , Dimensions , ViewPropTypes } from 'react-native' ;
3+ import {
4+ View ,
5+ TextInput ,
6+ StyleSheet ,
7+ Dimensions ,
8+ ViewPropTypes ,
9+ } from 'react-native' ;
410import _ from 'lodash' ;
511
612// if ViewPropTypes is not defined fall back to View.propType (to support RN < 0.44)
@@ -22,9 +28,8 @@ export default class ConfirmationCodeInput extends Component {
2228 codeInputStyle : TextInput . propTypes . style ,
2329 containerStyle : viewPropTypes . style ,
2430 onFulfill : PropTypes . func ,
25- onCodeChange : PropTypes . func ,
2631 } ;
27-
32+
2833 static defaultProps = {
2934 codeLength : 5 ,
3035 inputPosition : 'center' ,
@@ -38,45 +43,51 @@ export default class ConfirmationCodeInput extends Component {
3843 compareWithCode : '' ,
3944 ignoreCase : false ,
4045 } ;
41-
46+
4247 constructor ( props ) {
4348 super ( props ) ;
44-
49+
4550 this . state = {
4651 codeArr : new Array ( this . props . codeLength ) . fill ( '' ) ,
47- currentIndex : 0
52+ currentIndex : 0 ,
4853 } ;
49-
54+
5055 this . codeInputRefs = [ ] ;
5156 }
52-
57+
5358 componentDidMount ( ) {
5459 const { compareWithCode, codeLength, inputPosition } = this . props ;
5560 if ( compareWithCode && compareWithCode . length !== codeLength ) {
56- console . error ( "Invalid props: compareWith length is not equal to codeLength" ) ;
61+ console . error (
62+ 'Invalid props: compareWith length is not equal to codeLength'
63+ ) ;
5764 }
58-
59- if ( _ . indexOf ( [ 'center' , 'left' , 'right' , 'full-width' ] , inputPosition ) === - 1 ) {
60- console . error ( 'Invalid input position. Must be in: center, left, right, full' ) ;
65+
66+ if (
67+ _ . indexOf ( [ 'center' , 'left' , 'right' , 'full-width' ] , inputPosition ) === - 1
68+ ) {
69+ console . error (
70+ 'Invalid input position. Must be in: center, left, right, full'
71+ ) ;
6172 }
6273 }
63-
74+
6475 clear ( ) {
6576 this . setState ( {
6677 codeArr : new Array ( this . props . codeLength ) . fill ( '' ) ,
67- currentIndex : 0
78+ currentIndex : 0 ,
6879 } ) ;
6980 this . _setFocus ( 0 ) ;
7081 }
71-
82+
7283 _setFocus ( index ) {
7384 this . codeInputRefs [ index ] . focus ( ) ;
7485 }
75-
86+
7687 _blur ( index ) {
7788 this . codeInputRefs [ index ] . blur ( ) ;
7889 }
79-
90+
8091 _onFocus ( index ) {
8192 let newCodeArr = _ . clone ( this . state . codeArr ) ;
8293 const currentEmptyIndex = _ . findIndex ( newCodeArr , c => ! c ) ;
@@ -88,155 +99,182 @@ export default class ConfirmationCodeInput extends Component {
8899 newCodeArr [ i ] = '' ;
89100 }
90101 }
91-
102+
92103 this . setState ( {
93104 codeArr : newCodeArr ,
94- currentIndex : index
95- } )
105+ currentIndex : index ,
106+ } ) ;
96107 }
97-
108+
98109 _isMatchingCode ( code , compareWithCode , ignoreCase = false ) {
99110 if ( ignoreCase ) {
100111 return code . toLowerCase ( ) == compareWithCode . toLowerCase ( ) ;
101112 }
102113 return code == compareWithCode ;
103114 }
104-
115+
105116 _getContainerStyle ( size , position ) {
106117 switch ( position ) {
107118 case 'left' :
108119 return {
109120 justifyContent : 'flex-start' ,
110- height : size
121+ height : size ,
111122 } ;
112123 case 'center' :
113124 return {
114125 justifyContent : 'center' ,
115- height : size
126+ height : size ,
116127 } ;
117128 case 'right' :
118129 return {
119130 justifyContent : 'flex-end' ,
120- height : size
131+ height : size ,
121132 } ;
122133 default :
123134 return {
124135 justifyContent : 'space-between' ,
125- height : size
126- }
136+ height : size ,
137+ } ;
127138 }
128139 }
129-
140+
130141 _getInputSpaceStyle ( space ) {
131142 const { inputPosition } = this . props ;
132143 switch ( inputPosition ) {
133144 case 'left' :
134145 return {
135- marginRight : space
146+ marginRight : space ,
136147 } ;
137148 case 'center' :
138149 return {
139- marginRight : space / 2 ,
140- marginLeft : space / 2
150+ marginRight : space / 2 ,
151+ marginLeft : space / 2 ,
141152 } ;
142153 case 'right' :
143154 return {
144- marginLeft : space
155+ marginLeft : space ,
145156 } ;
146157 default :
147158 return {
148159 marginRight : 0 ,
149- marginLeft : 0
160+ marginLeft : 0 ,
150161 } ;
151162 }
152163 }
153-
164+
154165 _getClassStyle ( className , active ) {
155166 const { cellBorderWidth, activeColor, inactiveColor, space } = this . props ;
156167 let classStyle = {
157168 ...this . _getInputSpaceStyle ( space ) ,
158- color : activeColor
169+ color : activeColor ,
159170 } ;
160-
171+
161172 switch ( className ) {
162173 case 'clear' :
163174 return _ . merge ( classStyle , { borderWidth : 0 } ) ;
164175 case 'border-box' :
165176 return _ . merge ( classStyle , {
166177 borderWidth : cellBorderWidth ,
167- borderColor : ( active ? activeColor : inactiveColor )
178+ borderColor : active ? activeColor : inactiveColor ,
168179 } ) ;
169180 case 'border-circle' :
170181 return _ . merge ( classStyle , {
171182 borderWidth : cellBorderWidth ,
172183 borderRadius : 50 ,
173- borderColor : ( active ? activeColor : inactiveColor )
184+ borderColor : active ? activeColor : inactiveColor ,
174185 } ) ;
175186 case 'border-b' :
176187 return _ . merge ( classStyle , {
177188 borderBottomWidth : cellBorderWidth ,
178- borderColor : ( active ? activeColor : inactiveColor ) ,
189+ borderColor : active ? activeColor : inactiveColor ,
179190 } ) ;
180191 case 'border-b-t' :
181192 return _ . merge ( classStyle , {
182193 borderTopWidth : cellBorderWidth ,
183194 borderBottomWidth : cellBorderWidth ,
184- borderColor : ( active ? activeColor : inactiveColor )
195+ borderColor : active ? activeColor : inactiveColor ,
185196 } ) ;
186197 case 'border-l-r' :
187198 return _ . merge ( classStyle , {
188199 borderLeftWidth : cellBorderWidth ,
189200 borderRightWidth : cellBorderWidth ,
190- borderColor : ( active ? activeColor : inactiveColor )
201+ borderColor : active ? activeColor : inactiveColor ,
191202 } ) ;
192203 default :
193204 return className ;
194205 }
195206 }
196-
207+
197208 _onKeyPress ( e ) {
198209 if ( e . nativeEvent . key === 'Backspace' ) {
199210 const { currentIndex } = this . state ;
200- let newCodeArr = _ . clone ( this . state . codeArr ) ;
201211 const nextIndex = currentIndex > 0 ? currentIndex - 1 : 0 ;
202- for ( const i in newCodeArr ) {
203- if ( i >= nextIndex ) {
204- newCodeArr [ i ] = '' ;
205- }
206- }
207- this . props . onCodeChange ( newCodeArr . join ( '' ) )
208212 this . _setFocus ( nextIndex ) ;
209213 }
210214 }
211-
212- _onInputCode ( character , index ) {
213- const { codeLength, onFulfill, compareWithCode, ignoreCase, onCodeChange } = this . props ;
215+
216+ /** synthesizes the input characters based on the keyboard type removing invalid characters */
217+ _synthesizeInput = characters => {
218+ const { keyboardType } = this . props ;
219+ if ( keyboardType === 'numeric' ) {
220+ return characters . replace ( / \D / g, '' ) ;
221+ }
222+ return characters ;
223+ } ;
224+
225+ _onInputCode ( baseCharacters , baseIndex ) {
226+ const {
227+ codeLength,
228+ onFulfill,
229+ compareWithCode,
230+ ignoreCase,
231+ keyboardType,
232+ } = this . props ;
233+
234+ const characters = this . _synthesizeInput ( baseCharacters ) . substring (
235+ 0 ,
236+ codeLength - baseIndex
237+ ) ;
238+
214239 let newCodeArr = _ . clone ( this . state . codeArr ) ;
215- newCodeArr [ index ] = character ;
216-
217- if ( index == codeLength - 1 ) {
218- const code = newCodeArr . join ( '' ) ;
219-
240+ for (
241+ let i = baseIndex , j = 0 ;
242+ i < codeLength && j < characters . length ;
243+ i ++ , j ++
244+ ) {
245+ newCodeArr [ i ] = characters [ j ] ;
246+ }
247+
248+ /** caret position */
249+ let index = baseIndex + characters . length - 1 ;
250+
251+ /** constructed plain code */
252+ const code = newCodeArr . join ( '' ) ;
253+ if ( index === codeLength - 1 && code . length === codeLength ) {
220254 if ( compareWithCode ) {
221- const isMatching = this . _isMatchingCode ( code , compareWithCode , ignoreCase ) ;
255+ const isMatching = this . _isMatchingCode (
256+ code ,
257+ compareWithCode ,
258+ ignoreCase
259+ ) ;
222260 onFulfill ( isMatching , code ) ;
223261 ! isMatching && this . clear ( ) ;
224262 } else {
225263 onFulfill ( code ) ;
226264 }
227265 this . _blur ( this . state . currentIndex ) ;
228266 } else {
229- this . _setFocus ( this . state . currentIndex + 1 ) ;
267+ this . _setFocus ( index + 1 ) ;
230268 }
231-
269+
232270 this . setState ( prevState => {
233271 return {
234272 codeArr : newCodeArr ,
235- currentIndex : prevState . currentIndex + 1
273+ currentIndex : index + 1 ,
236274 } ;
237- } , ( ) => { onCodeChange ( newCodeArr . join ( '' ) ) } ) ;
275+ } ) ;
238276 }
239-
277+
240278 render ( ) {
241279 const {
242280 codeLength,
@@ -246,14 +284,14 @@ export default class ConfirmationCodeInput extends Component {
246284 autoFocus,
247285 className,
248286 size,
249- activeColor
287+ activeColor,
250288 } = this . props ;
251-
289+
252290 const initialCodeInputStyle = {
253291 width : size ,
254- height : size
292+ height : size ,
255293 } ;
256-
294+
257295 let codeInputs = [ ] ;
258296 for ( let i = 0 ; i < codeLength ; i ++ ) {
259297 const id = i ;
@@ -262,10 +300,10 @@ export default class ConfirmationCodeInput extends Component {
262300 key = { id }
263301 ref = { ref => ( this . codeInputRefs [ id ] = ref ) }
264302 style = { [
265- styles . codeInput ,
266- initialCodeInputStyle ,
303+ styles . codeInput ,
304+ initialCodeInputStyle ,
267305 this . _getClassStyle ( className , this . state . currentIndex == id ) ,
268- codeInputStyle
306+ codeInputStyle ,
269307 ] }
270308 underlineColorAndroid = "transparent"
271309 selectionColor = { activeColor }
@@ -274,16 +312,23 @@ export default class ConfirmationCodeInput extends Component {
274312 { ...this . props }
275313 autoFocus = { autoFocus && id == 0 }
276314 onFocus = { ( ) => this . _onFocus ( id ) }
277- value = { this . state . codeArr [ id ] ? this . state . codeArr [ id ] . toString ( ) : '' }
315+ value = {
316+ this . state . codeArr [ id ] ? this . state . codeArr [ id ] . toString ( ) : ''
317+ }
278318 onChangeText = { text => this . _onInputCode ( text , id ) }
279- onKeyPress = { ( e ) => this . _onKeyPress ( e ) }
280- maxLength = { 1 }
319+ onKeyPress = { e => this . _onKeyPress ( e ) }
281320 />
282- )
321+ ) ;
283322 }
284-
323+
285324 return (
286- < View style = { [ styles . container , this . _getContainerStyle ( size , inputPosition ) , containerStyle ] } >
325+ < View
326+ style = { [
327+ styles . container ,
328+ this . _getContainerStyle ( size , inputPosition ) ,
329+ containerStyle ,
330+ ] }
331+ >
287332 { codeInputs }
288333 </ View >
289334 ) ;
@@ -294,11 +339,11 @@ const styles = StyleSheet.create({
294339 container : {
295340 flex : 1 ,
296341 flexDirection : 'row' ,
297- marginTop : 20
342+ marginTop : 20 ,
298343 } ,
299344 codeInput : {
300345 backgroundColor : 'transparent' ,
301346 textAlign : 'center' ,
302- padding : 0
303- }
347+ padding : 0 ,
348+ } ,
304349} ) ;
0 commit comments