Skip to content

Commit 5592092

Browse files
committed
Add value param, enable clear value, and fixes maxLength when input is number type
1 parent 1d1edd5 commit 5592092

File tree

4 files changed

+81
-49
lines changed

4 files changed

+81
-49
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"demo:prod": "webpack --mode production",
1212
"format": "prettier --write '**/*.{js,jsx}'",
1313
"lint": "eslint '**/*.{js,jsx}' --quiet",
14+
"lint:fix": "eslint --fix 'src/**/*.{js,jsx}'",
1415
"prepublish": "npm run build",
1516
"predeploy": "npm run demo:prod",
1617
"deploy": "gh-pages -d demo"

src/demo/index.jsx

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class Demo extends Component {
2424
this.setState({ [e.target.name]: e.target.value });
2525
};
2626

27+
clearOtp = () => {
28+
this.setState({ otp: '' });
29+
};
30+
2731
handleCheck = e => {
2832
const { name } = e.target;
2933
this.setState(prevState => ({ [name]: !prevState[name] }));
@@ -70,6 +74,19 @@ class Demo extends Component {
7074
/>
7175
</label>
7276
</div>
77+
<div className="side-bar__segment">
78+
<label htmlFor="value">
79+
value
80+
<input
81+
id="value"
82+
maxLength={numInputs}
83+
name="otp"
84+
type="text"
85+
value={otp}
86+
onChange={this.handleChange}
87+
/>
88+
</label>
89+
</div>
7390
<div className="side-bar__segment">
7491
<label htmlFor="disabled">
7592
<input
@@ -134,14 +151,24 @@ class Demo extends Component {
134151
separator={<span>{separator}</span>}
135152
isInputNum={isInputNum}
136153
shouldAutoFocus
154+
value={otp}
137155
/>
138156
</div>
139-
<button
140-
className="btn margin-top--large"
141-
disabled={otp.length < numInputs}
142-
>
143-
Get OTP
144-
</button>
157+
<div className="btn-row">
158+
<button
159+
className="btn margin-top--large"
160+
type="button"
161+
onClick={this.clearOtp}
162+
>
163+
Clear
164+
</button>
165+
<button
166+
className="btn margin-top--large"
167+
disabled={otp.length < numInputs}
168+
>
169+
Get OTP
170+
</button>
171+
</div>
145172
</form>
146173
</div>
147174
</div>

src/demo/styles.css

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,19 @@ a {
8282
align-items: center;
8383
}
8484

85+
.btn-row {
86+
display: flex;
87+
flex-direction: row;
88+
}
89+
8590
.btn {
8691
background-color: #3273dc;
8792
border: none;
8893
padding: 0.5rem 1rem;
8994
color: white;
9095
border-radius: 4px;
9196
cursor: pointer;
97+
margin: 1rem;
9298
}
9399

94100
.btn:disabled {
@@ -110,4 +116,4 @@ a {
110116

111117
.error {
112118
border: 1px solid red !important;
113-
}
119+
}

src/lib/index.jsx

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Props = {
2020
errorStyle?: Object,
2121
shouldAutoFocus?: boolean,
2222
isInputNum?: boolean,
23+
value?: string,
2324
};
2425

2526
type State = {
@@ -123,28 +124,30 @@ class OtpInput extends Component<Props, State> {
123124
onChange: (otp: number): void => console.log(otp),
124125
isDisabled: false,
125126
shouldAutoFocus: false,
127+
value: '',
126128
};
127129

128130
state = {
129131
activeInput: 0,
130-
otp: [],
131132
};
132133

134+
getOtpValue = () => (
135+
this.props.value ? this.props.value.toString().split('') : []
136+
);
137+
133138
// Helper to return OTP from input
134-
getOtp = () => {
139+
handleOtpChange = (otp: string[]) => {
135140
const { onChange, isInputNum } = this.props;
136-
const otp = this.state.otp.join('');
137-
onChange(isInputNum ? Number(otp) : otp);
141+
const otpValue = otp.join('');
142+
onChange(isInputNum ? Number(otpValue) : otpValue);
138143
};
139144

140145
// Focus on input by index
141146
focusInput = (input: number) => {
142147
const { numInputs } = this.props;
143148
const activeInput = Math.max(Math.min(numInputs - 1, input), 0);
144149

145-
this.setState({
146-
activeInput,
147-
});
150+
this.setState({ activeInput });
148151
};
149152

150153
// Focus on next input
@@ -161,20 +164,19 @@ class OtpInput extends Component<Props, State> {
161164

162165
// Change OTP value at focused input
163166
changeCodeAtFocus = (value: string) => {
164-
const { activeInput, otp } = this.state;
167+
const { activeInput } = this.state;
168+
const otp = this.getOtpValue();
165169
otp[activeInput] = value[0];
166170

167-
this.setState({
168-
otp,
169-
});
170-
this.getOtp();
171+
this.handleOtpChange(otp);
171172
};
172173

173174
// Handle pasted OTP
174175
handleOnPaste = (e: Object) => {
175176
e.preventDefault();
176177
const { numInputs } = this.props;
177-
const { activeInput, otp } = this.state;
178+
const { activeInput } = this.state;
179+
const otp = this.getOtpValue();
178180

179181
// Get pastedData in an array of max size (num of inputs - current position)
180182
const pastedData = e.clipboardData
@@ -189,11 +191,7 @@ class OtpInput extends Component<Props, State> {
189191
}
190192
}
191193

192-
this.setState({
193-
otp,
194-
});
195-
196-
this.getOtp();
194+
this.handleOtpChange(otp);
197195
};
198196

199197
handleOnChange = (e: Object) => {
@@ -203,31 +201,31 @@ class OtpInput extends Component<Props, State> {
203201

204202
// Handle cases of backspace, delete, left arrow, right arrow
205203
handleOnKeyDown = (e: Object) => {
206-
switch (e.keyCode) {
207-
case BACKSPACE:
208-
e.preventDefault();
209-
this.changeCodeAtFocus('');
210-
this.focusPrevInput();
211-
break;
212-
case DELETE:
213-
e.preventDefault();
214-
this.changeCodeAtFocus('');
215-
break;
216-
case LEFT_ARROW:
217-
e.preventDefault();
218-
this.focusPrevInput();
219-
break;
220-
case RIGHT_ARROW:
221-
e.preventDefault();
222-
this.focusNextInput();
223-
break;
224-
default:
225-
break;
204+
if (e.keyCode === BACKSPACE || e.key === 'Backspace') {
205+
e.preventDefault();
206+
this.changeCodeAtFocus('');
207+
this.focusPrevInput();
208+
} else if (e.keyCode === DELETE || e.key === 'Delete') {
209+
e.preventDefault();
210+
this.changeCodeAtFocus('');
211+
} else if (e.keyCode === LEFT_ARROW || e.key === 'ArrowLeft') {
212+
e.preventDefault();
213+
this.focusPrevInput();
214+
} else if (e.keyCode === RIGHT_ARROW || e.key === 'ArrowRight') {
215+
e.preventDefault();
216+
this.focusNextInput();
226217
}
227218
};
228219

220+
checkLength = (e: Object) => {
221+
if (e.target.value.length > 1) {
222+
e.preventDefault();
223+
this.focusNextInput();
224+
}
225+
}
226+
229227
renderInputs = () => {
230-
const { activeInput, otp } = this.state;
228+
const { activeInput } = this.state;
231229
const {
232230
numInputs,
233231
inputStyle,
@@ -240,6 +238,7 @@ class OtpInput extends Component<Props, State> {
240238
shouldAutoFocus,
241239
isInputNum,
242240
} = this.props;
241+
const otp = this.getOtpValue();
243242
const inputs = [];
244243

245244
for (let i = 0; i < numInputs; i++) {
@@ -250,11 +249,10 @@ class OtpInput extends Component<Props, State> {
250249
value={otp && otp[i]}
251250
onChange={this.handleOnChange}
252251
onKeyDown={this.handleOnKeyDown}
252+
onInput={this.checkLength}
253253
onPaste={this.handleOnPaste}
254254
onFocus={e => {
255-
this.setState({
256-
activeInput: i,
257-
});
255+
this.setState({ activeInput: i });
258256
e.target.select();
259257
}}
260258
onBlur={() => this.setState({ activeInput: -1 })}

0 commit comments

Comments
 (0)