react-smart-otp
Version:
A lightweight, accessible, and slot based fully customizable React OTP input component
3 lines (2 loc) • 1.54 kB
JavaScript
import{jsx as t,jsxs as e}from"react/jsx-runtime";import n,{useState as o,useRef as a,useEffect as r}from"react";const c=({value:c,slots:p={},inputType:l="text",length:s,onChange:i,defaultFocus:u=!1})=>{const[m,f]=o(c),[g,d]=o(u?0:-1),h=a([]);r(()=>{void 0===c&&console.warn("ReactOtp: Missing `value` prop — component will not be controlled properly.")},[]);const v=p.Container?t=>n.createElement(p.Container,t):e=>t("div",{className:"otp-container",...e}),y=p.Input?t=>n.createElement(p.Input,t):e=>t("input",{className:"otp-input",...e}),x=p.Separator?t=>n.createElement(p.Separator,t):e=>t("span",{...e});r(()=>{c!==m&&f(c)},[c]),r(()=>{u&&(h.current[0]?.focus(),d(0))},[u]),r(()=>{g<s&&h.current[g]?.focus()},[g,s]);const C=t=>{t.preventDefault();const e=t.clipboardData.getData("text").slice(0,s);e&&(f(e),i(e),d(e.length-1))},D=Array.from({length:s},(t,e)=>m[e]||"");return t(v,{children:D.map((o,a)=>{const r={ref:t=>{h.current[a]=t},"data-testid":`otp-input-${a}`,"aria-label":`OTP input ${a+1} of ${s}`,id:`otpinput-${a}`,autoComplete:"off",type:l,maxLength:1,value:o,onChange:t=>((t,e)=>{f(n=>{const o=n.split("");o[e]=t.slice(-1);const a=o.join("");return i(a),t&&e<s-1&&d(e+1),a})})(t.target.value,a),onKeyDown:t=>((t,e)=>{if("Backspace"===t.key){t.preventDefault();const n=m.split("");n[e]="";const o=n.join("");f(o),i(o),e>0&&d(e-1)}})(t,a),onPaste:C,onFocus:t=>((t,e)=>{t.target.select(),d(e)})(t,a)};return e(n.Fragment,{children:[t(y,{...r}),a<s-1&&t(x,{})]},a)})})};export{c as ReactOtp};
//# sourceMappingURL=index.js.map