ورودی رمز یکبار مصرف (Input OTP)
کامپوننت ورودی رمز یکبار مصرف با قابلیت کپی و پیست و پشتیبانی کامل از RTL و دسترسیپذیری.
نصب (Installation)
1npx @quark-lab/rad-ui add input-otpنمونهها (Examples)
استفاده پایه (Basic Usage)
استفاده ساده از ورودی OTP با ۶ رقم.
مشاهده کد
1import {
2 InputOTP,
3 InputOTPGroup,
4 InputOTPSlot,
5 InputOTPSeparator,
6} from "@/components/ui/input-otp";
7
8export default function BasicExample() {
9 return (
10 <InputOTP maxLength={6}>
11 <InputOTPGroup>
12 <InputOTPSlot index={0} />
13 <InputOTPSlot index={1} />
14 <InputOTPSlot index={2} />
15 </InputOTPGroup>
16 <InputOTPSeparator />
17 <InputOTPGroup>
18 <InputOTPSlot index={3} />
19 <InputOTPSlot index={4} />
20 <InputOTPSlot index={5} />
21 </InputOTPGroup>
22 </InputOTP>
23 );
24}
25فقط اعداد (Digits Only)
محدود کردن ورودی به اعداد با استفاده از الگو.
مشاهده کد
1import {
2 InputOTP,
3 InputOTPGroup,
4 InputOTPSlot,
5 InputOTPSeparator,
6 REGEXP_ONLY_DIGITS,
7} from "@/components/ui/input-otp";
8import { Label } from "@/components/ui/label";
9
10export default function PatternExample() {
11 return (
12 <div className="space-y-2 flex flex-col items-center">
13 <Label>رمز عددی</Label>
14 <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
15 <InputOTPGroup>
16 <InputOTPSlot index={0} />
17 <InputOTPSlot index={1} />
18 <InputOTPSlot index={2} />
19 </InputOTPGroup>
20 <InputOTPSeparator />
21 <InputOTPGroup>
22 <InputOTPSlot index={3} />
23 <InputOTPSlot index={4} />
24 <InputOTPSlot index={5} />
25 </InputOTPGroup>
26 </InputOTP>
27 </div>
28 );
29}
30جداکننده (Separator)
افزودن جداکننده بین گروههای ورودی.
مشاهده کد
1import {
2 InputOTP,
3 InputOTPGroup,
4 InputOTPSlot,
5 InputOTPSeparator,
6} from "@/components/ui/input-otp";
7
8export default function SeparatorExample() {
9 return (
10 <InputOTP maxLength={4}>
11 <InputOTPGroup>
12 <InputOTPSlot index={0} />
13 <InputOTPSlot index={1} />
14 </InputOTPGroup>
15 <InputOTPSeparator />
16 <InputOTPGroup>
17 <InputOTPSlot index={2} />
18 <InputOTPSlot index={3} />
19 </InputOTPGroup>
20 </InputOTP>
21 );
22}
23غیرفعال (Disabled)
غیرفعال کردن ورودی.
مشاهده کد
1import {
2 InputOTP,
3 InputOTPGroup,
4 InputOTPSlot,
5 InputOTPSeparator,
6} from "@/components/ui/input-otp";
7
8export default function DisabledExample() {
9 return (
10 <InputOTP maxLength={6} disabled>
11 <InputOTPGroup>
12 <InputOTPSlot index={0} />
13 <InputOTPSlot index={1} />
14 <InputOTPSlot index={2} />
15 </InputOTPGroup>
16 <InputOTPSeparator />
17 <InputOTPGroup>
18 <InputOTPSlot index={3} />
19 <InputOTPSlot index={4} />
20 <InputOTPSlot index={5} />
21 </InputOTPGroup>
22 </InputOTP>
23 );
24}
25کنترلشده (Controlled)
کنترل مقدار ورودی و نمایش آن.
رمز یکبار مصرف خود را وارد کنید.
مشاهده کد
1import { useState } from "react";
2import {
3 InputOTP,
4 InputOTPGroup,
5 InputOTPSlot,
6 InputOTPSeparator,
7} from "@/components/ui/input-otp";
8
9const PERSIAN_DIGITS = ["۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"];
10function toPersianDigits(str: string): string {
11 return str.replace(/\d/g, (d) => PERSIAN_DIGITS[parseInt(d)] ?? d);
12}
13
14export default function ControlledExample() {
15 const [value, setValue] = useState("");
16
17 return (
18 <div className="flex flex-col items-center gap-4">
19 <InputOTP
20 maxLength={6}
21 value={value}
22 onChange={setValue}
23 >
24 <InputOTPGroup>
25 <InputOTPSlot index={0} />
26 <InputOTPSlot index={1} />
27 <InputOTPSlot index={2} />
28 </InputOTPGroup>
29 <InputOTPSeparator />
30 <InputOTPGroup>
31 <InputOTPSlot index={3} />
32 <InputOTPSlot index={4} />
33 <InputOTPSlot index={5} />
34 </InputOTPGroup>
35 </InputOTP>
36 <p className="text-sm text-muted-foreground text-center">
37 {value === "" ? (
38 <>رمز یکبار مصرف خود را وارد کنید.</>
39 ) : (
40 <>
41 مقدار وارد شده:{" "}
42 <span className="font-mono font-bold" dir="ltr">
43 {toPersianDigits(value)}
44 </span>
45 </>
46 )}
47 </p>
48 </div>
49 );
50}
51نامعتبر (Invalid)
نمایش حالت خطا برای ورودی.
رمز وارد شده نامعتبر است. لطفاً دوباره تلاش کنید.
مشاهده کد
1import {
2 InputOTP,
3 InputOTPGroup,
4 InputOTPSlot,
5 InputOTPSeparator,
6} from "@/components/ui/input-otp";
7
8export default function InvalidExample() {
9 return (
10 <div className="flex flex-col items-center gap-3">
11 <InputOTP maxLength={6} defaultValue="000000">
12 <InputOTPGroup>
13 <InputOTPSlot
14 index={0}
15 className="border-destructive text-destructive"
16 />
17 <InputOTPSlot
18 index={1}
19 className="border-destructive text-destructive"
20 />
21 <InputOTPSlot
22 index={2}
23 className="border-destructive text-destructive"
24 />
25 </InputOTPGroup>
26 <InputOTPSeparator />
27 <InputOTPGroup>
28 <InputOTPSlot
29 index={3}
30 className="border-destructive text-destructive"
31 />
32 <InputOTPSlot
33 index={4}
34 className="border-destructive text-destructive"
35 />
36 <InputOTPSlot
37 index={5}
38 className="border-destructive text-destructive"
39 />
40 </InputOTPGroup>
41 </InputOTP>
42 <p className="text-sm text-destructive">
43 رمز وارد شده نامعتبر است. لطفاً دوباره تلاش کنید.
44 </p>
45 </div>
46 );
47}
48چهار رقمی (Four Digits)
الگوی رایج برای کد PIN چهار رقمی.
مشاهده کد
1import {
2 InputOTP,
3 InputOTPGroup,
4 InputOTPSlot,
5 REGEXP_ONLY_DIGITS,
6} from "@/components/ui/input-otp";
7import { Label } from "@/components/ui/label";
8
9export default function FourDigitsExample() {
10 return (
11 <div className="space-y-2 flex flex-col items-center">
12 <Label>کد PIN</Label>
13 <InputOTP maxLength={4} pattern={REGEXP_ONLY_DIGITS}>
14 <InputOTPGroup>
15 <InputOTPSlot index={0} />
16 <InputOTPSlot index={1} />
17 <InputOTPSlot index={2} />
18 <InputOTPSlot index={3} />
19 </InputOTPGroup>
20 </InputOTP>
21 </div>
22 );
23}
24حروف و اعداد (Alphanumeric)
پذیرش حروف و اعداد در ورودی.
مشاهده کد
1import {
2 InputOTP,
3 InputOTPGroup,
4 InputOTPSlot,
5 InputOTPSeparator,
6 REGEXP_ONLY_DIGITS_AND_CHARS,
7} from "@/components/ui/input-otp";
8
9export default function AlphanumericExample() {
10 return (
11 <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
12 <InputOTPGroup>
13 <InputOTPSlot index={0} />
14 <InputOTPSlot index={1} />
15 <InputOTPSlot index={2} />
16 </InputOTPGroup>
17 <InputOTPSeparator />
18 <InputOTPGroup>
19 <InputOTPSlot index={3} />
20 <InputOTPSlot index={4} />
21 <InputOTPSlot index={5} />
22 </InputOTPGroup>
23 </InputOTP>
24 );
25}
26نمونه کاربردی (Practical Example)
فرم تأیید (Verification Form)
نمونه کامل فرم تأیید رمز یکبار مصرف.
تأیید ورود
کد تأیید ارسال شده به ایمیل m@example.com را وارد کنید.
مشکلی در ورود دارید؟ تماس با پشتیبانی
مشاهده کد
1import { useState } from "react";
2import {
3 InputOTP,
4 InputOTPGroup,
5 InputOTPSlot,
6 InputOTPSeparator,
7} from "@/components/ui/input-otp";
8import { Label } from "@/components/ui/label";
9import { Button } from "@/components/ui/button";
10
11export default function FormExample() {
12 const [formValue, setFormValue] = useState("");
13 const [formSubmitted, setFormSubmitted] = useState(false);
14
15 return (
16 <div className="max-w-md mx-auto">
17 <div className="space-y-6">
18 <div className="text-center space-y-2">
19 <h3 className="text-xl font-semibold">تأیید ورود</h3>
20 <p className="text-sm text-muted-foreground">
21 کد تأیید ارسال شده به ایمیل{" "}
22 <span className="font-medium text-foreground" dir="ltr">
23 m@example.com
24 </span>{" "}
25 را وارد کنید.
26 </p>
27 </div>
28
29 <form
30 onSubmit={(e) => {
31 e.preventDefault();
32 setFormSubmitted(true);
33 }}
34 className="space-y-6"
35 >
36 <div className="space-y-2">
37 <Label htmlFor="otp-form">کد تأیید</Label>
38 <div className="flex justify-center">
39 <InputOTP
40 id="otp-form"
41 maxLength={6}
42 value={formValue}
43 onChange={setFormValue}
44 >
45 <InputOTPGroup>
46 <InputOTPSlot index={0} />
47 <InputOTPSlot index={1} />
48 <InputOTPSlot index={2} />
49 </InputOTPGroup>
50 <InputOTPSeparator />
51 <InputOTPGroup>
52 <InputOTPSlot index={3} />
53 <InputOTPSlot index={4} />
54 <InputOTPSlot index={5} />
55 </InputOTPGroup>
56 </InputOTP>
57 </div>
58 <p className="text-xs text-muted-foreground text-center">
59 کد ارسال نشد؟{" "}
60 <button
61 type="button"
62 className="text-primary underline hover:text-primary/80"
63 >
64 ارسال مجدد
65 </button>
66 </p>
67 </div>
68
69 <Button
70 type="submit"
71 className="w-full"
72 disabled={formValue.length < 6}
73 >
74 تأیید
75 </Button>
76
77 {formSubmitted && formValue.length === 6 && (
78 <p className="text-sm text-center text-primary">
79 کد با موفقیت ارسال شد!
80 </p>
81 )}
82 </form>
83
84 <p className="text-xs text-center text-muted-foreground">
85 مشکلی در ورود دارید؟{" "}
86 <a href="#" className="text-primary underline">
87 تماس با پشتیبانی
88 </a>
89 </p>
90 </div>
91 </div>
92 );
93}
94پشتیبانی RTL
کامپوننت Input OTP به طور کامل از جهتگیری راستچین پشتیبانی میکند. از آنجا که ورود ارقام همیشه از چپ به راست است، ورودی خود دارای جهت dir="ltr" است اما برچسبها و توضیحات اطراف آن از جهت سند پیروی میکنند.
- حاشیههای اسلاتها از ویژگیهای منطقی CSS استفاده میکنند (
border-inline-start،border-inline-end) - گوشههای گرد از
rounded-s-mdوrounded-e-mdاستفاده میکنند - برچسبها و متون راهنما با RTL هماهنگ هستند
مرجع API (API Reference)
InputOTP
کامپوننت اصلی ورودی رمز یکبار مصرف.
| پراپ (Prop) | نوع (Type) | پیشفرض (Default) | توضیحات (Description) |
|---|---|---|---|
maxLength | number | - | حداکثر تعداد کاراکترهای ورودی (اجباری) |
pattern | string | RegExp | - | الگوی مجاز برای ورودی |
value | string | - | مقدار کنترلشده ورودی |
onChange | (value: string) => void | - | تابع فراخوانی هنگام تغییر مقدار |
disabled | boolean | false | غیرفعال کردن ورودی |
containerClassName | string | - | کلاسهای CSS برای محفظه بیرونی |
className | string | - | کلاسهای CSS سفارشی |
InputOTPSlot
کامپوننت نمایش هر رقم یا کاراکتر.
| پراپ (Prop) | نوع (Type) | پیشفرض (Default) | توضیحات (Description) |
|---|---|---|---|
index | number | - | شماره اندیس اسلات در ورودی OTP (اجباری) |
className | string | - | کلاسهای CSS سفارشی |
ثابتهای الگو
| ثابت (Constant) | توضیحات |
|---|---|
REGEXP_ONLY_DIGITS | فقط ارقام (۰ تا ۹) |
REGEXP_ONLY_DIGITS_AND_CHARS | ارقام و حروف انگلیسی |