کاستومایز
کدِ یکبارمصرفCode Slots
ورودیِ کد یکبارمصرف؛ هر رقم با فنر توی خونهی خودش مینشینه، خطا خونهها رو از آخر به اول خالی میکنه و موفقیت پردهای میکشه که تیک وسطش بالا میآد.
وابستگیها
نوع تعامل
روی ردیف کلیک کن و رقم بزن؛ هر رقم با جهشِ فنری توی خونهی بعدی مینشینه. کلِ کد رو هم میتونی یهجا پیست کنی — رقمها پلهپله مینشینن. با پر شدنِ آخرین خونه، `onComplete` صدا زده میشه و هر تصمیمی که دربارهی کد گرفته باشی دیده میشه: پذیرش یعنی پردهی اکسنت از وسط باز میشه و تیک بالا میآد، رد یعنی خونهها از آخر به اول خالی میشن و بعد کد پاک میشه. این کامپوننت خودش تصمیم نمیگیره که کد درسته یا نه — اون کارِ توئه.
پراپها
گزینههایی که میتونی به کامپوننت بدی؛ ستارهدارها اجباریاند.
lengthnumberپیشفرض: 6شمارِ خانهها.
valuestringپیشفرض: undefinedکدِ controlled. رقمی که از بیرون میرسه با پلکان مینشینه و رقمی که میره خالی میشه.
defaultValuestringپیشفرض: ""کدِ اولیهی uncontrolled؛ از همون اول نشسته رندر میشه.
onChange(code: string) => voidبا هر ویرایش صدا زده میشه — از جمله پاکشدنِ آخرِ یک رد.
onComplete(code: string) => voidیه بار، وقتی آخرین خونه پر میشه.
status"idle" | "error" | "success"پیشفرض: "idle"idle · error · successخطا خونهها رو از آخر به اول زیرِ تهرنگِ خطر خالی میکنه و کد رو پاک میکنه؛ موفقیت پرشدگیها رو توی یه پرده یکی میکنه و ورودی رو قفل میکنه.
نصب
۱. این آدرس رو یکبار توی components.json بذار تا shadcn مخزن فارسیفای رو بشناسه.
{
"registries": {
"@farsify": "https://farsify.ir/r/{name}.json"
}
}۲. بعد با دستورِ زیر کامپوننت رو نصب کن.
npx shadcn@latest add @farsify/code-slotsپیشنیاز: Tailwind CSS v4 و صفحهٔ راستبهچپ dir="rtl".
نمونه
"use client";
import { useState } from "react";
import { CodeSlots, type CodeSlotsStatus } from "@/components/farsify/code-slots";
/** چند میلیثانیه تا رسیدنِ پاسخ، و چند میلیثانیه پاسخ بماند. */
const ANSWER_MS = 350;
const RESET_MS = 1800;
export function OneTimeCode() {
const [code, setCode] = useState("");
const [status, setStatus] = useState<CodeSlotsStatus>("idle");
return (
<CodeSlots
value={code}
status={status}
onChange={(next) => {
setCode(next);
// هر ویرایش، حالت را به idle برمیگرداند — وگرنه بعد از یک پذیرش
// کامپوننت قفل میماند و کاربر نمیتواند دوباره تایپ کند
setStatus("idle");
}}
onComplete={async (done) => {
// اینجا تصمیم میگیرید کد درست است یا نه
const ok = await verify(done);
setTimeout(() => {
setStatus(ok ? "success" : "error");
if (ok) {
setTimeout(() => {
setCode("");
setStatus("idle");
}, RESET_MS);
}
}, ANSWER_MS);
}}
/>
);
}
// کدِ هشترقمی، پوشیده، با خانههای بزرگتر
<CodeSlots length={8} mask slotSize={56} gap={10} />
// نشستنی خشک و همزمان — بدونِ جهشِ فنر و بدونِ خطِ پلهای
<CodeSlots bounce={0} cascade={0} rise={0} />
// در حالتِ خطا، خودِ کامپوننت خانهها را از آخر به اول خالی میکند و تهش
// onChange("") میدهد؛ لازم نیست خودتان پاکش کنید
<CodeSlots value={code} status="error" onChange={setCode} />نکتهها
- این کامپوننت تصمیم نمیگیره که کد درسته یا نه — کدِ درست کجاست رو تو میدونی. `onComplete` رو بگیر و بعد از پاسخِ سرور `status` رو بذار.
- پیست کار میکنه: کلِ کد رو کپی کن و روی ردیف بچسبون، رقمها با پلکان مینشینن. کیبوردِ خودکارِ پیامک (`autocomplete="one-time-code"`) هم وصله.
- ورودیِ فارسی هم پذیرفته میشه — «۱۲۳» و «١٢٣» هر دو به همون کدِ لاتین تبدیل میشن. مقدارِ `value` و `onChange` همیشه لاتینه، چون دادهست نه نمایش.
- خونههای پرشده از خونههای خالی قابلِ تشخیصان ولی **قابلِ پرش نیستن**: کلیک روی هر خونه، مکاننما رو تا اولین خونهی خالی میبره و نه جلوتر.
- `cascade` رو صفر کن تا همهی رقمها همزمان بنشینن؛ بالا ببرش تا خطِ پلهای واضحتر شه. بالای ۴۰ms روی کدِ ششرقمی کند بهنظر میرسه.
- این پورت **راستبهچپ نمیشه** و این عمدیه: هندسهی این کامپوننت فیزیکیه (مکاننما با `translateX` حرکت میکنه و ضربه با `clientX` به خونه تبدیل میشه). فقط متن و ارقام فارسی شدن — همون قاعدهی `jelly-radio`.
- برای اینکه کاربر بفهمه کدِ اشتباه رو رد کردی، `status` رو روی `error` بذار و **بعدش خودت** بعد از تمومشدنِ خالیشدن به `idle` برگردون — کامپوننت تهش `onChange('')` میده و همون نشانهی پایانِ کارشه.
مجوز
کد بعد از نصب مالِ خودته؛ هر تغییری خواستی بده و توی پروژهی خودت نگهش دار. برای پروژههای شخصی و تجاری آزاده، حتی اگه کدت بسته باشه. فقط اسم و لینک فارسیفای رو یه جا توی پروژه نگه دار.