From 84cf47c7333b4d44a3be8dcfe67351162d702954 Mon Sep 17 00:00:00 2001 From: andreferi31 Date: Thu, 7 May 2020 20:13:33 +0700 Subject: [PATCH 1/5] add conditional styling in circle className --- components/ConfirmationCodeInput.js | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/components/ConfirmationCodeInput.js b/components/ConfirmationCodeInput.js index 1d9f9b2..766415f 100644 --- a/components/ConfirmationCodeInput.js +++ b/components/ConfirmationCodeInput.js @@ -22,7 +22,6 @@ export default class ConfirmationCodeInput extends Component { codeInputStyle: TextInput.propTypes.style, containerStyle: viewPropTypes.style, onFulfill: PropTypes.func, - onCodeChange: PropTypes.func, }; static defaultProps = { @@ -36,7 +35,7 @@ export default class ConfirmationCodeInput extends Component { inactiveColor: 'rgba(255, 255, 255, 0.2)', space: 8, compareWithCode: '', - ignoreCase: false, + ignoreCase: false }; constructor(props) { @@ -197,20 +196,13 @@ export default class ConfirmationCodeInput extends Component { _onKeyPress(e) { if (e.nativeEvent.key === 'Backspace') { const { currentIndex } = this.state; - let newCodeArr = _.clone(this.state.codeArr); const nextIndex = currentIndex > 0 ? currentIndex - 1 : 0; - for (const i in newCodeArr) { - if (i >= nextIndex) { - newCodeArr[i] = ''; - } - } - this.props.onCodeChange(newCodeArr.join('')) this._setFocus(nextIndex); } } _onInputCode(character, index) { - const { codeLength, onFulfill, compareWithCode, ignoreCase, onCodeChange } = this.props; + const { codeLength, onFulfill, compareWithCode, ignoreCase } = this.props; let newCodeArr = _.clone(this.state.codeArr); newCodeArr[index] = character; @@ -234,7 +226,7 @@ export default class ConfirmationCodeInput extends Component { codeArr: newCodeArr, currentIndex: prevState.currentIndex + 1 }; - }, () => { onCodeChange(newCodeArr.join('')) }); + }); } render() { @@ -265,7 +257,10 @@ export default class ConfirmationCodeInput extends Component { styles.codeInput, initialCodeInputStyle, this._getClassStyle(className, this.state.currentIndex == id), - codeInputStyle + codeInputStyle, + (className == 'border-circle' && this.state.codeArr[id]) && { + backgroundColor: '#e5e5e5' + } ]} underlineColorAndroid="transparent" selectionColor={activeColor} @@ -274,7 +269,7 @@ export default class ConfirmationCodeInput extends Component { {...this.props} autoFocus={autoFocus && id == 0} onFocus={() => this._onFocus(id)} - value={this.state.codeArr[id] ? this.state.codeArr[id].toString() : ''} + value={this.state.codeArr[id] && className !== 'border-circle' ? this.state.codeArr[id].toString() : ''} onChangeText={text => this._onInputCode(text, id)} onKeyPress={(e) => this._onKeyPress(e)} maxLength={1} From d4dfd5a856832b82f19f1b41e7f7c2191fab60a7 Mon Sep 17 00:00:00 2001 From: Andre Feri Saputra <44439185+andreferi3@users.noreply.github.com> Date: Thu, 7 May 2020 20:15:10 +0700 Subject: [PATCH 2/5] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d7b2aa8..7ed1762 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "react-native-confirmation-code-input", + "name": "@andre/react-native-confirmation-code-input", "version": "1.0.1", "description": "A react-native component to input confirmation code for both Android and IOS", "main": "index.js", From 33f61dbde05fb33b5f23f44e4c95b993b5d698a2 Mon Sep 17 00:00:00 2001 From: andreferi31 Date: Thu, 7 May 2020 20:18:23 +0700 Subject: [PATCH 3/5] update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d7b2aa8..7ed1762 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "react-native-confirmation-code-input", + "name": "@andre/react-native-confirmation-code-input", "version": "1.0.1", "description": "A react-native component to input confirmation code for both Android and IOS", "main": "index.js", From 9d65216eac6737d1841bb62f2523cb466aa3b708 Mon Sep 17 00:00:00 2001 From: andreferi31 Date: Tue, 12 May 2020 19:30:55 +0700 Subject: [PATCH 4/5] add feature copy paste and fill circle classname --- .npmignore | 3 +- README.md | 4 +- components/ConfirmationCodeInput.js | 172 +++++++++++++++++++--------- package-lock.json | 46 ++++++++ package.json | 11 +- 5 files changed, 177 insertions(+), 59 deletions(-) create mode 100644 package-lock.json diff --git a/.npmignore b/.npmignore index 32239cd..206811a 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,4 @@ .idea .npmignore -example \ No newline at end of file +example +node_modules/ \ No newline at end of file diff --git a/README.md b/README.md index 01352fb..d637430 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,14 @@ A react-native confirmation code input for both IOS and Android ## Installation ```sh -npm install react-native-confirmation-code-input --save +yarn add @andreferi/react-native-confirmation-code-input --save ``` ## Usage ### Basic Import this module: ```javascript -import CodeInput from 'react-native-confirmation-code-input'; +import CodeInput from '@andreferi/react-native-confirmation-code-input'; ``` Use as a component and style it: ```javascript diff --git a/components/ConfirmationCodeInput.js b/components/ConfirmationCodeInput.js index 766415f..6a8a265 100644 --- a/components/ConfirmationCodeInput.js +++ b/components/ConfirmationCodeInput.js @@ -1,6 +1,6 @@ -import React, {Component} from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { View, TextInput, StyleSheet, Dimensions, ViewPropTypes } from 'react-native'; +import { View, TextInput, StyleSheet, Dimensions, ViewPropTypes, Clipboard } from 'react-native'; import _ from 'lodash'; // if ViewPropTypes is not defined fall back to View.propType (to support RN < 0.44) @@ -23,7 +23,7 @@ export default class ConfirmationCodeInput extends Component { containerStyle: viewPropTypes.style, onFulfill: PropTypes.func, }; - + static defaultProps = { codeLength: 5, inputPosition: 'center', @@ -37,29 +37,30 @@ export default class ConfirmationCodeInput extends Component { compareWithCode: '', ignoreCase: false }; - + constructor(props) { super(props); - + this.state = { codeArr: new Array(this.props.codeLength).fill(''), - currentIndex: 0 + currentIndex: 0, + isPasted: false }; - + this.codeInputRefs = []; } - + componentDidMount() { const { compareWithCode, codeLength, inputPosition } = this.props; if (compareWithCode && compareWithCode.length !== codeLength) { console.error("Invalid props: compareWith length is not equal to codeLength"); } - + if (_.indexOf(['center', 'left', 'right', 'full-width'], inputPosition) === -1) { console.error('Invalid input position. Must be in: center, left, right, full'); } } - + clear() { this.setState({ codeArr: new Array(this.props.codeLength).fill(''), @@ -67,15 +68,15 @@ export default class ConfirmationCodeInput extends Component { }); this._setFocus(0); } - + _setFocus(index) { this.codeInputRefs[index].focus(); } - + _blur(index) { this.codeInputRefs[index].blur(); } - + _onFocus(index) { let newCodeArr = _.clone(this.state.codeArr); const currentEmptyIndex = _.findIndex(newCodeArr, c => !c); @@ -87,20 +88,20 @@ export default class ConfirmationCodeInput extends Component { newCodeArr[i] = ''; } } - + this.setState({ codeArr: newCodeArr, currentIndex: index }) } - + _isMatchingCode(code, compareWithCode, ignoreCase = false) { if (ignoreCase) { return code.toLowerCase() == compareWithCode.toLowerCase(); } return code == compareWithCode; } - + _getContainerStyle(size, position) { switch (position) { case 'left': @@ -125,7 +126,7 @@ export default class ConfirmationCodeInput extends Component { } } } - + _getInputSpaceStyle(space) { const { inputPosition } = this.props; switch (inputPosition) { @@ -135,8 +136,8 @@ export default class ConfirmationCodeInput extends Component { }; case 'center': return { - marginRight: space/2, - marginLeft: space/2 + marginRight: space / 2, + marginLeft: space / 2 }; case 'right': return { @@ -149,14 +150,14 @@ export default class ConfirmationCodeInput extends Component { }; } } - + _getClassStyle(className, active) { const { cellBorderWidth, activeColor, inactiveColor, space } = this.props; let classStyle = { ...this._getInputSpaceStyle(space), color: activeColor }; - + switch (className) { case 'clear': return _.merge(classStyle, { borderWidth: 0 }); @@ -192,7 +193,7 @@ export default class ConfirmationCodeInput extends Component { return className; } } - + _onKeyPress(e) { if (e.nativeEvent.key === 'Backspace') { const { currentIndex } = this.state; @@ -200,35 +201,103 @@ export default class ConfirmationCodeInput extends Component { this._setFocus(nextIndex); } } - - _onInputCode(character, index) { - const { codeLength, onFulfill, compareWithCode, ignoreCase } = this.props; + + async _onInputCode(character, index) { + var copiedContent = await Clipboard.getString(); + let pastedArr = copiedContent.split('') let newCodeArr = _.clone(this.state.codeArr); - newCodeArr[index] = character; - - if (index == codeLength - 1) { - const code = newCodeArr.join(''); - - if (compareWithCode) { - const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase); - onFulfill(isMatching, code); - !isMatching && this.clear(); + let isPasted = newCodeArr.includes(pastedArr[0]) + const { codeLength, onFulfill, compareWithCode, ignoreCase } = this.props; + console.warn('copiedContent ', pastedArr[0]) + console.warn(isPasted) + if (!isPasted && index == 0 && character.length > 1) { + console.warn('masuk on pasted') + console.warn('character : ', character) + console.warn('index : ', index) + for (let i in pastedArr) { + newCodeArr[i] = pastedArr[i] + } + + if (copiedContent.length == codeLength) { + const code = newCodeArr.join(''); + console.warn('validated') + + if (compareWithCode) { + const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase); + onFulfill(isMatching, code); + !isMatching && this.clear(); + } else { + onFulfill(code); + } + this._blur(this.state.currentIndex); } else { - onFulfill(code); + this._setFocus(pastedArr.length) } - this._blur(this.state.currentIndex); + + this.setState({ + codeArr: newCodeArr, + currentIndex: pastedArr.length, + isPasted: true + }) } else { - this._setFocus(this.state.currentIndex + 1); - } - - this.setState(prevState => { - return { + const i = this.state.currentIndex + newCodeArr[i] = character + + if (i == codeLength-1) { + const code = newCodeArr.join(''); + console.warn('validated') + + if (compareWithCode) { + const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase); + onFulfill(isMatching, code); + !isMatching && this.clear(); + } else { + onFulfill(code); + } + this._blur(i); + } else { + this._setFocus(i+1) + } + + this.setState(prevState => ({ codeArr: newCodeArr, - currentIndex: prevState.currentIndex + 1 - }; - }); + currentIndex: prevState.currentIndex + 1, + isPasted: true + })) + + // const i = pastedArr.length + // newCodeArr[i] = character + + console.warn(newCodeArr) + }// } else { + // console.warn('no pasted') + // const i = this.state.isPasted ? this.state.currentIndex : index + // newCodeArr[i] = character; + + // if (i == codeLength - 1) { + // const code = newCodeArr.join(''); + + // if (compareWithCode) { + // const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase); + // onFulfill(isMatching, code); + // !isMatching && this.clear(); + // } else { + // onFulfill(code); + // } + // this._blur(this.state.currentIndex); + // } else { + // this._setFocus(this.state.currentIndex + 1); + // } + + // this.setState(prevState => { + // return { + // codeArr: newCodeArr, + // currentIndex: prevState.currentIndex + 1 + // }; + // }); + // } } - + render() { const { codeLength, @@ -240,12 +309,12 @@ export default class ConfirmationCodeInput extends Component { size, activeColor } = this.props; - + const initialCodeInputStyle = { width: size, height: size }; - + let codeInputs = []; for (let i = 0; i < codeLength; i++) { const id = i; @@ -254,8 +323,8 @@ export default class ConfirmationCodeInput extends Component { key={id} ref={ref => (this.codeInputRefs[id] = ref)} style={[ - styles.codeInput, - initialCodeInputStyle, + styles.codeInput, + initialCodeInputStyle, this._getClassStyle(className, this.state.currentIndex == id), codeInputStyle, (className == 'border-circle' && this.state.codeArr[id]) && { @@ -263,7 +332,7 @@ export default class ConfirmationCodeInput extends Component { } ]} underlineColorAndroid="transparent" - selectionColor={activeColor} + selectionColor={'white'} keyboardType={'name-phone-pad'} returnKeyType={'done'} {...this.props} @@ -272,11 +341,10 @@ export default class ConfirmationCodeInput extends Component { value={this.state.codeArr[id] && className !== 'border-circle' ? this.state.codeArr[id].toString() : ''} onChangeText={text => this._onInputCode(text, id)} onKeyPress={(e) => this._onKeyPress(e)} - maxLength={1} /> ) } - + return ( {codeInputs} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b893226 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,46 @@ +{ + "name": "@andre/react-native-confirmation-code-input", + "version": "1.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + } + } +} diff --git a/package.json b/package.json index 7ed1762..5fe09f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@andre/react-native-confirmation-code-input", - "version": "1.0.1", + "name": "@andreferi/react-native-confirmation-code-input", + "version": "1.1.0", "description": "A react-native component to input confirmation code for both Android and IOS", "main": "index.js", "scripts": { @@ -30,8 +30,11 @@ }, "repository": { "type": "git", - "url": "https://github.com/ttdung11t2/react-native-confirmation-code-input.git" + "url": "git+https://github.com/ttdung11t2/react-native-confirmation-code-input.git" }, "author": "Dung Tran ", - "license": "MIT" + "license": "MIT", + "directories": { + "example": "example" + } } From 51fc06f67a49de3de0145b60c40643ece1ab68c0 Mon Sep 17 00:00:00 2001 From: andreferi31 Date: Sun, 23 Aug 2020 09:33:41 +0700 Subject: [PATCH 5/5] Chore: Remove unused variables and comments --- components/ConfirmationCodeInput.js | 50 +++-------------------------- index.d.ts | 2 +- 2 files changed, 6 insertions(+), 46 deletions(-) diff --git a/components/ConfirmationCodeInput.js b/components/ConfirmationCodeInput.js index 6a8a265..11a55e7 100644 --- a/components/ConfirmationCodeInput.js +++ b/components/ConfirmationCodeInput.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { View, TextInput, StyleSheet, Dimensions, ViewPropTypes, Clipboard } from 'react-native'; +import { View, TextInput, StyleSheet, ViewPropTypes, Clipboard } from 'react-native'; import _ from 'lodash'; // if ViewPropTypes is not defined fall back to View.propType (to support RN < 0.44) @@ -208,19 +208,13 @@ export default class ConfirmationCodeInput extends Component { let newCodeArr = _.clone(this.state.codeArr); let isPasted = newCodeArr.includes(pastedArr[0]) const { codeLength, onFulfill, compareWithCode, ignoreCase } = this.props; - console.warn('copiedContent ', pastedArr[0]) - console.warn(isPasted) if (!isPasted && index == 0 && character.length > 1) { - console.warn('masuk on pasted') - console.warn('character : ', character) - console.warn('index : ', index) for (let i in pastedArr) { newCodeArr[i] = pastedArr[i] } if (copiedContent.length == codeLength) { const code = newCodeArr.join(''); - console.warn('validated') if (compareWithCode) { const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase); @@ -243,9 +237,8 @@ export default class ConfirmationCodeInput extends Component { const i = this.state.currentIndex newCodeArr[i] = character - if (i == codeLength-1) { + if (i == codeLength - 1) { const code = newCodeArr.join(''); - console.warn('validated') if (compareWithCode) { const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase); @@ -256,7 +249,7 @@ export default class ConfirmationCodeInput extends Component { } this._blur(i); } else { - this._setFocus(i+1) + this._setFocus(i + 1) } this.setState(prevState => ({ @@ -264,38 +257,7 @@ export default class ConfirmationCodeInput extends Component { currentIndex: prevState.currentIndex + 1, isPasted: true })) - - // const i = pastedArr.length - // newCodeArr[i] = character - - console.warn(newCodeArr) - }// } else { - // console.warn('no pasted') - // const i = this.state.isPasted ? this.state.currentIndex : index - // newCodeArr[i] = character; - - // if (i == codeLength - 1) { - // const code = newCodeArr.join(''); - - // if (compareWithCode) { - // const isMatching = this._isMatchingCode(code, compareWithCode, ignoreCase); - // onFulfill(isMatching, code); - // !isMatching && this.clear(); - // } else { - // onFulfill(code); - // } - // this._blur(this.state.currentIndex); - // } else { - // this._setFocus(this.state.currentIndex + 1); - // } - - // this.setState(prevState => { - // return { - // codeArr: newCodeArr, - // currentIndex: prevState.currentIndex + 1 - // }; - // }); - // } + } } render() { @@ -306,9 +268,7 @@ export default class ConfirmationCodeInput extends Component { inputPosition, autoFocus, className, - size, - activeColor - } = this.props; + size } = this.props; const initialCodeInputStyle = { width: size, diff --git a/index.d.ts b/index.d.ts index 3473a5d..fe44263 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,7 +1,7 @@ import * as React from "react"; import * as ReactNative from "react-native"; -declare module "react-native-confirmation-code-input" { +declare module "@andreferi/react-native-confirmation-code-input" { type InputPositions = 'left' | 'right' | 'center' | 'full-width'; type ClassNames = 'border-box' | 'border-circle' | 'border-b' | 'border-b-t' | 'border-l-r';