کدِ یک‌بارمصرفCode Slots

ورودیِ کد یک‌بارمصرف؛ هر رقم با فنر توی خونه‌ی خودش می‌نشینه، خطا خونه‌ها رو از آخر به اول خالی می‌کنه و موفقیت پرده‌ای می‌کشه که تیک وسطش بالا می‌آد.

محمدجواد

ساخته شده توسط محمدجواد

سازنده فارسی‌فای، بچه دولوپر

وابستگی‌ها

motion

نوع تعامل

روی ردیف کلیک کن و رقم بزن؛ هر رقم با جهشِ فنری توی خونه‌ی بعدی می‌نشینه. کلِ کد رو هم می‌تونی یه‌جا پیست کنی — رقم‌ها پله‌پله می‌نشینن. با پر شدنِ آخرین خونه، `onComplete` صدا زده می‌شه و هر تصمیمی که درباره‌ی کد گرفته باشی دیده می‌شه: پذیرش یعنی پرده‌ی اکسنت از وسط باز می‌شه و تیک بالا می‌آد، رد یعنی خونه‌ها از آخر به اول خالی می‌شن و بعد کد پاک می‌شه. این کامپوننت خودش تصمیم نمی‌گیره که کد درسته یا نه — اون کارِ توئه.

پراپ‌ها

گزینه‌هایی که می‌تونی به کامپوننت بدی؛ ستاره‌دارها اجباری‌اند.

length
numberپیش‌فرض: 6

شمارِ خانه‌ها.

value
stringپیش‌فرض: undefined

کدِ controlled. رقمی که از بیرون می‌رسه با پلکان می‌نشینه و رقمی که می‌ره خالی می‌شه.

defaultValue
stringپیش‌فرض: ""

کدِ اولیه‌ی uncontrolled؛ از همون اول نشسته رندر می‌شه.

onChange
(code: string) => void

با هر ویرایش صدا زده می‌شه — از جمله پاک‌شدنِ آخرِ یک رد.

onComplete
(code: string) => void

یه بار، وقتی آخرین خونه پر می‌شه.

status
"idle" | "error" | "success"پیش‌فرض: "idle"idle · error · success

خطا خونه‌ها رو از آخر به اول زیرِ ته‌رنگِ خطر خالی می‌کنه و کد رو پاک می‌کنه؛ موفقیت پرشدگی‌ها رو توی یه پرده یکی می‌کنه و ورودی رو قفل می‌کنه.

نصب

۱. این آدرس رو یک‌بار توی components.json بذار تا shadcn مخزن فارسی‌فای رو بشناسه.

components.jsonjson
{
  "registries": {
    "@farsify": "https://farsify.ir/r/{name}.json"
  }
}

۲. بعد با دستورِ زیر کامپوننت رو نصب کن.

npx shadcn@latest add @farsify/code-slots

پیش‌نیاز: Tailwind CSS v4 و صفحهٔ راست‌به‌چپ dir="rtl".

نمونه

code-slots.tsxtsx
"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('')` می‌ده و همون نشانه‌ی پایانِ کارشه.

مجوز

کد بعد از نصب مالِ خودته؛ هر تغییری خواستی بده و توی پروژه‌ی خودت نگهش دار. برای پروژه‌های شخصی و تجاری آزاده، حتی اگه کدت بسته باشه. فقط اسم و لینک فارسی‌فای رو یه جا توی پروژه نگه دار.