diff --git a/comps/form/field/type/TypeOTP.tsx b/comps/form/field/type/TypeOTP.tsx index f7f35a0..482f238 100755 --- a/comps/form/field/type/TypeOTP.tsx +++ b/comps/form/field/type/TypeOTP.tsx @@ -2,6 +2,8 @@ import { useLocal } from "lib/utils/use-local"; import { FC, useEffect } from "react"; import { FieldLocal, FieldProp, FMLocal } from "../../typings"; import { PropTypeInput } from "./TypeInput"; +import { InputOTP, InputOTPGroup, InputOTPSlot } from "lib/comps/ui/input-otp"; +import { REGEXP_ONLY_DIGITS } from "input-otp"; export const FieldOTP: FC<{ digit: number; @@ -11,81 +13,44 @@ export const FieldOTP: FC<{ arg: FieldProp; }> = ({ digit, fm, field }) => { const local = useLocal({ - otp: [] as string[], + otp: "", ref: [] as HTMLInputElement[], }); - if (local.otp.length === 0 && digit) { - for (let i = 0; i < digit; i++) { - local.otp.push(""); - } - } - return ( -
- {local.otp.map((item, idx) => ( - { - if (ref) local.ref[idx] = ref; - }} - onPaste={(e) => { - e.preventDefault(); - - var clipboardData = - e.clipboardData || (window as any).clipboardData; - var pastedData = clipboardData.getData("text"); - for (let i = 0; i < pastedData.length; i++) { - if (i >= local.otp.length) break; - local.otp[i] = pastedData[i]; - } - local.render(); - }} - onKeyDown={async (e) => { - if (e.key === "Backspace") { - let _idx = idx; - if (local.otp[_idx].length === 0) { - _idx--; - } - local.otp[_idx] = ""; - local.render(); - const ref = local.ref[Math.max(0, _idx - 1)]; - if (ref) { - ref.focus(); - } - } else if (parseInt(e.key) || e.key === "0") { - local.otp[idx] = e.key; - local.render(); - const ref = local.ref[idx + 1]; - if (ref) { - ref.focus(); - } - } - - const otp = local.otp.join(""); - fm.data[field.name] = otp; - if (otp.length === digit) { - fm.render(); - } - // local.render(); - }} - /> - ))} +
+ { + local.otp = value; + local.render(); + if (field.on_change) { + field.on_change({ value, name: field.name, fm }); + } + }} + pattern={REGEXP_ONLY_DIGITS} + inputMode="decimal" + > + + + + + + +
); }; diff --git a/comps/ui/input-otp.tsx b/comps/ui/input-otp.tsx new file mode 100755 index 0000000..4edde46 --- /dev/null +++ b/comps/ui/input-otp.tsx @@ -0,0 +1,69 @@ +import * as React from "react" +import { OTPInput, OTPInputContext } from "input-otp" +import { Dot } from "lucide-react" + +import { cn } from "@/utils" + +const InputOTP = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, containerClassName, ...props }, ref) => ( + +)) +InputOTP.displayName = "InputOTP" + +const InputOTPGroup = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> +>(({ className, ...props }, ref) => ( +
+)) +InputOTPGroup.displayName = "InputOTPGroup" + +const InputOTPSlot = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> & { index: number } +>(({ index, className, ...props }, ref) => { + const inputOTPContext = React.useContext(OTPInputContext) + const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index] + + return ( +
+ {char} + {hasFakeCaret && ( +
+
+
+ )} +
+ ) +}) +InputOTPSlot.displayName = "InputOTPSlot" + +const InputOTPSeparator = React.forwardRef< + React.ElementRef<"div">, + React.ComponentPropsWithoutRef<"div"> +>(({ ...props }, ref) => ( +
+ +
+)) +InputOTPSeparator.displayName = "InputOTPSeparator" + +export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }