fix otp
This commit is contained in:
parent
1453a545ab
commit
42eace5e4f
|
|
@ -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 (
|
||||
<div className="c-flex-1 c-flex c-justify-center c-items-center">
|
||||
{local.otp.map((item, idx) => (
|
||||
<input
|
||||
key={idx}
|
||||
<div
|
||||
className={cx(
|
||||
"c-rounded-md c-text-center",
|
||||
"c-flex-1 c-flex c-justify-center c-items-center",
|
||||
css`
|
||||
margin: 3px;
|
||||
font-size: 3em;
|
||||
padding: 0px 10px;
|
||||
width: 60px;
|
||||
height: 100px;
|
||||
border: 1px solid #ddd;
|
||||
background: white;
|
||||
.otp-single {
|
||||
height: 80px;
|
||||
width: 50px;
|
||||
font-size: 20px;
|
||||
}
|
||||
`
|
||||
)}
|
||||
>
|
||||
<InputOTP
|
||||
maxLength={4}
|
||||
value={local.otp}
|
||||
onChange={(value) => {
|
||||
local.otp = value;
|
||||
local.render();
|
||||
if (field.on_change) {
|
||||
field.on_change({ value, name: field.name, fm });
|
||||
}
|
||||
}}
|
||||
pattern={REGEXP_ONLY_DIGITS}
|
||||
inputMode="decimal"
|
||||
pattern="[0-9]*"
|
||||
value={item}
|
||||
ref={(ref) => {
|
||||
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();
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
>
|
||||
<InputOTPGroup>
|
||||
<InputOTPSlot index={0} className="otp-single" />
|
||||
<InputOTPSlot index={1} className="otp-single" />
|
||||
<InputOTPSlot index={2} className="otp-single" />
|
||||
<InputOTPSlot index={3} className="otp-single" />
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<typeof OTPInput>,
|
||||
React.ComponentPropsWithoutRef<typeof OTPInput>
|
||||
>(({ className, containerClassName, ...props }, ref) => (
|
||||
<OTPInput
|
||||
ref={ref}
|
||||
containerClassName={cn(
|
||||
"flex items-center gap-2 has-[:disabled]:opacity-50",
|
||||
containerClassName
|
||||
)}
|
||||
className={cn("disabled:c-cursor-not-allowed", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
InputOTP.displayName = "InputOTP"
|
||||
|
||||
const InputOTPGroup = React.forwardRef<
|
||||
React.ElementRef<"div">,
|
||||
React.ComponentPropsWithoutRef<"div">
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("c-flex c-items-center", className)} {...props} />
|
||||
))
|
||||
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 (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"c-relative c-flex c-h-10 c-w-10 c-items-center c-justify-center c-border-y c-border-r c-border-input c-text-sm c-transition-all first:c-rounded-l-md first:c-border-l last:c-rounded-r-md",
|
||||
isActive && "c-z-10 c-ring-2 c-ring-ring c-ring-offset-background",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{char}
|
||||
{hasFakeCaret && (
|
||||
<div className="c-pointer-events-none c-absolute c-inset-0 c-flex c-items-center c-justify-center">
|
||||
<div className="c-h-4 c-w-px c-animate-caret-blink c-bg-foreground c-duration-1000" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
InputOTPSlot.displayName = "InputOTPSlot"
|
||||
|
||||
const InputOTPSeparator = React.forwardRef<
|
||||
React.ElementRef<"div">,
|
||||
React.ComponentPropsWithoutRef<"div">
|
||||
>(({ ...props }, ref) => (
|
||||
<div ref={ref} role="separator" {...props}>
|
||||
<Dot />
|
||||
</div>
|
||||
))
|
||||
InputOTPSeparator.displayName = "InputOTPSeparator"
|
||||
|
||||
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
|
||||
Loading…
Reference in New Issue