From 618854da2c961face464b235744bb6470565476b Mon Sep 17 00:00:00 2001 From: "Cristhyan D. Marchena" Date: Fri, 2 Feb 2024 12:27:41 -0500 Subject: [PATCH] feat: add \onBlur\ and \onFocus\ callback support --- README.md | 20 +++++++++++++++++++- example/src/App.tsx | 2 ++ src/index.tsx | 17 +++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6931cd7..09a9a4d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ A fully customizable, one-time password input component for the web built with R ![see here](https://media.giphy.com/media/lN98dFU6h3oP0wWS5x/giphy.gif) [Live Demo](https://devfolioco.github.io/react-otp-input) - ## Installation @@ -24,6 +25,7 @@ npm install --save react-otp-input ``` ### Still using v2? + No problem! You can find the documentation for v2 [here](https://github.com/devfolioco/react-otp-input/tree/v2.4.0) #### Basic usage: @@ -73,6 +75,13 @@ export default function App() { The function will get two arguments: inputProps and index. inputProps is an object that contains all the props that should be passed to the input being rendered (Overriding these props is not recommended because it might lead to some unexpected behaviour). index is the index of the input being rendered. + + onFocus + function + false + none + Called when OTP has received focus. The index of the input gaining focus is passed as the first argument + onChange function @@ -96,6 +105,13 @@ const handlePaste: React.ClipboardEventHandler = (event) => { + + onBlur + function + false + none + Called when OTP has lost focus. + value string / number @@ -155,7 +171,9 @@ const handlePaste: React.ClipboardEventHandler = (event) => { ### ⚠️ Warning + Do not override the following props on the input component that you return from the `renderInput` prop. Doing so might lead to unexpected behaviour. + - `ref` - `value` - `onChange` diff --git a/example/src/App.tsx b/example/src/App.tsx index b08cce1..5620c98 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -110,7 +110,9 @@ function App() { console.log(`OTP: Input #${index + 1} has gained focus!`)} onChange={handleOTPChange} + onBlur={(index) => console.log(`OTP: Input #${index + 1} has lost focus!`)} renderSeparator={{separator}} value={otp} placeholder={placeholder} diff --git a/src/index.tsx b/src/index.tsx index 3afd218..8144f48 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -29,10 +29,14 @@ interface OTPInputProps { value?: string; /** Number of OTP inputs to be rendered */ numInputs?: number; + /** Callback to be called when the OTP has received focued */ + onFocus?: (index: number) => void; /** Callback to be called when the OTP value changes */ onChange: (otp: string) => void; /** Callback to be called when pasting content into the component */ onPaste?: (event: React.ClipboardEvent) => void; + /** Callback to be called when the OTP has lost focus */ + onBlur?: (index: number) => void; /** Function to render the input */ renderInput: (inputProps: InputProps, index: number) => React.ReactNode; /** Whether the first input should be auto focused */ @@ -56,8 +60,10 @@ const isStyleObject = (obj: unknown) => typeof obj === 'object' && obj !== null; const OTPInput = ({ value = '', numInputs = 4, + onFocus, onChange, onPaste, + onBlur, renderInput, shouldAutoFocus = false, inputType = 'text', @@ -145,10 +151,17 @@ const OTPInput = ({ const handleFocus = (event: React.FocusEvent) => (index: number) => { setActiveInput(index); event.target.select(); + + if (!inputRefs.current.includes(event.relatedTarget as HTMLInputElement)) { + onFocus?.(index); + } }; - const handleBlur = () => { + const handleBlur = (event: React.FocusEvent) => (index: number) => { setActiveInput(activeInput - 1); + if (!inputRefs.current.includes(event.relatedTarget as HTMLInputElement)) { + onBlur?.(index); + } }; const handleKeyDown = (event: React.KeyboardEvent) => { @@ -247,7 +260,7 @@ const OTPInput = ({ ref: (element) => (inputRefs.current[index] = element), onChange: handleChange, onFocus: (event) => handleFocus(event)(index), - onBlur: handleBlur, + onBlur: (event) => handleBlur(event)(index), onKeyDown: handleKeyDown, onPaste: handlePaste, autoComplete: 'off',