UNPKG

nextuiq

Version:

NextUIQ is a modern, lightweight, and developer-friendly UI component library for React and Next.js. Built with TypeScript and Tailwind CSS, it offers customizable, accessible, and performance-optimized components with built-in dark mode, theme customizat

130 lines (127 loc) 4.85 kB
import { j as jsxRuntimeExports } from './index46.mjs'; import { forwardRef, useState, useRef } from 'react'; const OTPInput = forwardRef(({ length = 6, value = "", onChange, disabled = false, error = false, hint, className = "", label = "Verification code", description = "Enter the verification code", inputType = "numeric", ...props }, ref) => { const [otp, setOTP] = useState(value.split("")); const inputRefs = useRef([]); const groupId = useRef(`otp-${Math.random().toString(36).slice(2, 11)}`); const handleChange = (index, value2) => { const validationPatterns = { numeric: /^\d*$/, alphanumeric: /^[a-zA-Z0-9]*$/, any: /^.*$/ }; if (!validationPatterns[inputType].test(value2)) return; const newOTP = [...otp]; newOTP[index] = value2; setOTP(newOTP); onChange?.(newOTP.join("")); if (value2 && index < length - 1) { inputRefs.current[index + 1]?.focus(); } }; const handlePaste = (e) => { e.preventDefault(); const pastedData = e.clipboardData.getData("text").slice(0, length); const validationPatterns = { numeric: /^\d*$/, alphanumeric: /^[a-zA-Z0-9]*$/, any: /^.*$/ }; if (!validationPatterns[inputType].test(pastedData)) return; const newOTP = [...otp]; pastedData.split("").forEach((char, index) => { newOTP[index] = char; if (inputRefs.current[index]) { inputRefs.current[index].value = char; } }); setOTP(newOTP); onChange?.(newOTP.join("")); inputRefs.current[Math.min(pastedData.length, length) - 1]?.focus(); }; const handleKeyDown = (index, e) => { if (e.key === "Backspace" && !otp[index] && index > 0) { inputRefs.current[index - 1]?.focus(); } else if (e.key === "ArrowLeft" && index > 0) { inputRefs.current[index - 1]?.focus(); } else if (e.key === "ArrowRight" && index < length - 1) { inputRefs.current[index + 1]?.focus(); } }; return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { ref, className: "space-y-2", children: [ /* @__PURE__ */ jsxRuntimeExports.jsxs( "div", { role: "group", "aria-labelledby": `${groupId.current}-label`, "aria-describedby": `${groupId.current}-description ${error ? `${groupId.current}-error` : ""}`, className: "space-y-2", children: [ /* @__PURE__ */ jsxRuntimeExports.jsx( "label", { id: `${groupId.current}-label`, className: "block text-sm font-medium text-[oklch(var(--theme-foreground))]", children: label } ), description && /* @__PURE__ */ jsxRuntimeExports.jsx( "p", { id: `${groupId.current}-description`, className: "text-sm text-[oklch(var(--theme-muted-foreground))]", children: description } ), /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `flex gap-2 ${className}`, children: Array.from({ length }, (_, index) => /* @__PURE__ */ jsxRuntimeExports.jsx( "input", { ref: (el) => { if (el) inputRefs.current[index] = el; }, type: "text", inputMode: inputType === "numeric" ? "numeric" : "text", pattern: inputType === "numeric" ? "\\d*" : void 0, maxLength: 1, value: otp[index] || "", onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), onPaste: handlePaste, disabled, "aria-label": `Digit ${index + 1} of ${length}`, ...props, className: `h-12 w-12 text-center rounded-lg border text-lg font-semibold shadow-sm bg-[oklch(var(--theme-background))] text-[oklch(var(--theme-foreground))] focus:outline-none focus:ring-2 ${disabled ? "text-[oklch(var(--theme-muted-foreground))] border-[oklch(var(--theme-border))] bg-[oklch(var(--theme-muted))] cursor-not-allowed" : error ? "border-[oklch(var(--theme-destructive))] focus:ring-[oklch(var(--theme-destructive)/0.2)]" : "border-[oklch(var(--theme-border))] focus:border-[oklch(var(--theme-ring))] focus:ring-[oklch(var(--theme-ring)/0.2)]"}` }, index )) }) ] } ), hint && /* @__PURE__ */ jsxRuntimeExports.jsx( "p", { id: `${groupId.current}-error`, className: `text-xs ${error ? "text-[oklch(var(--theme-destructive))]" : "text-[oklch(var(--theme-muted-foreground))]"}`, children: hint } ) ] }); }); OTPInput.displayName = "OTPInput"; export { OTPInput, OTPInput as default };