Input otpstandard
인증 번호처럼 정해진 자릿수를 칸으로 나눠 받는 일회용 비밀번호 입력(one-time password input, OTP)입니다. shadcn base-nova 의 `input-otp` 는 `input-otp` 패키지를 감쌉니다.
설치
pnpm
pnpm dlx shadcn@4.23.0 add @uikit/input-otpDefault
input-otp-example.tsx
"use client"
import * as React from "react"import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
import { Example, ExampleWrapper,} from "@/components/example"import { Button } from "@/components/ui/button"import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle,} from "@/components/ui/card"import { Field, FieldDescription, FieldError, FieldLabel,} from "@/components/ui/field"import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot,} from "@/components/ui/input-otp"import { RefreshCwIcon } from "lucide-react"
export default function InputOTPExample() { return ( <ExampleWrapper> <InputOTPForm /> <InputOTPSimple /> <InputOTPPattern /> <InputOTPWithSeparator /> <InputOTPAlphanumeric /> <InputOTPDisabled /> <InputOTPFourDigits /> <InputOTPInvalid /> </ExampleWrapper> )}
function InputOTPSimple() { return ( <Example title="Simple"> <Field> <FieldLabel htmlFor="simple">Simple</FieldLabel> <InputOTP id="simple" maxLength={6}> <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </Field> </Example> )}
function InputOTPPattern() { return ( <Example title="Digits Only"> <Field> <FieldLabel htmlFor="digits-only">Digits Only</FieldLabel> <InputOTP id="digits-only" maxLength={6} pattern={REGEXP_ONLY_DIGITS}> <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </Field> </Example> )}
function InputOTPWithSeparator() { const [value, setValue] = React.useState("123456")
return ( <Example title="With Separator"> <Field> <FieldLabel htmlFor="with-separator">With Separator</FieldLabel> <InputOTP id="with-separator" maxLength={6} value={value} onChange={setValue} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </Field> </Example> )}
function InputOTPAlphanumeric() { return ( <Example title="Alphanumeric"> <Field> <FieldLabel htmlFor="alphanumeric">Alphanumeric</FieldLabel> <FieldDescription>Accepts both letters and numbers.</FieldDescription> <InputOTP id="alphanumeric" maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS} > <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </Field> </Example> )}
function InputOTPDisabled() { return ( <Example title="Disabled"> <Field> <FieldLabel htmlFor="disabled">Disabled</FieldLabel> <InputOTP id="disabled" maxLength={6} disabled value="123456"> <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> </Field> </Example> )}
function InputOTPFourDigits() { return ( <Example title="4 Digits"> <Field> <FieldLabel htmlFor="four-digits">4 Digits</FieldLabel> <FieldDescription>Common pattern for PIN codes.</FieldDescription> <InputOTP id="four-digits" maxLength={4} pattern={REGEXP_ONLY_DIGITS}> <InputOTPGroup> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> <InputOTPSlot index={3} /> </InputOTPGroup> </InputOTP> </Field> </Example> )}
function InputOTPInvalid() { const [value, setValue] = React.useState("000000")
return ( <Example title="Invalid State"> <Field> <FieldLabel htmlFor="invalid">Invalid State</FieldLabel> <FieldDescription> Example showing the invalid error state. </FieldDescription> <InputOTP id="invalid" maxLength={6} value={value} onChange={setValue}> <InputOTPGroup> <InputOTPSlot index={0} aria-invalid /> <InputOTPSlot index={1} aria-invalid /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={2} aria-invalid /> <InputOTPSlot index={3} aria-invalid /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot index={4} aria-invalid /> <InputOTPSlot index={5} aria-invalid /> </InputOTPGroup> </InputOTP> <FieldError errors={[{ message: "Invalid code. Please try again." }]} /> </Field> </Example> )}
function InputOTPForm() { return ( <Example title="Form"> <Card className="mx-auto max-w-md"> <CardHeader> <CardTitle>Verify your login</CardTitle> <CardDescription> Enter the verification code we sent to your email address:{" "} <span className="font-medium">m@example.com</span>. </CardDescription> </CardHeader> <CardContent> <form> <Field> <div className="flex items-center justify-between"> <FieldLabel htmlFor="otp-verification"> Verification code </FieldLabel> <Button variant="outline" size="xs"> <RefreshCwIcon data-icon="inline-start" /> Resend Code </Button> </div> <InputOTP maxLength={6} id="otp-verification" required> <InputOTPGroup className="*:data-[slot=input-otp-slot]:text-xl style-vega:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-maia:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-mira:*:data-[slot=input-otp-slot]:w-11 style-luma:*:data-[slot=input-otp-slot]:h-16 style-luma:*:data-[slot=input-otp-slot]:w-12"> <InputOTPSlot index={0} /> <InputOTPSlot index={1} /> <InputOTPSlot index={2} /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup className="*:data-[slot=input-otp-slot]:text-xl style-vega:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-maia:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-mira:*:data-[slot=input-otp-slot]:w-11 style-luma:*:data-[slot=input-otp-slot]:h-16 style-luma:*:data-[slot=input-otp-slot]:w-12"> <InputOTPSlot index={3} /> <InputOTPSlot index={4} /> <InputOTPSlot index={5} /> </InputOTPGroup> </InputOTP> <FieldDescription> <a href="#">I no longer have access to this email address.</a> </FieldDescription> </Field> </form> </CardContent> <CardFooter className="flex-col gap-2"> <Button type="submit" className="w-full"> Verify </Button> <div className="text-sm text-muted-foreground"> Having trouble signing in?{" "} <a href="#" className="underline underline-offset-4 transition-colors hover:text-primary" > Contact support </a> </div> </CardFooter> </Card> </Example> )}