HextaUI

Input OTP

Yazmayı, yapıştırmayı ve SMS otomatik doldurmayı kabul eden tek kullanımlık kod slotları; kodları basamaklayarak getiren isteğe bağlı bir animasyon ve doğrulama için bir durum içerir.

Type or paste 123456 to pass. Anything else fails.

"use client"

import * as React from "react"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
  type InputOTPStatus,
} from "@/components/ui/input-otp"
import { Label } from "@/components/ui/label"

export function InputOTPDemo() {
  const [value, setValue] = React.useState("")
  const [status, setStatus] = React.useState<InputOTPStatus>("idle")
  const timer = React.useRef<ReturnType<typeof setTimeout>>(undefined)

  React.useEffect(() => () => clearTimeout(timer.current), [])

  function verify(code: string) {
    setStatus("loading")
    clearTimeout(timer.current)
    timer.current = setTimeout(() => {
      if (code === "123456") {
        setStatus("success")
        return
      }
      setStatus("error")
      timer.current = setTimeout(() => {
        setValue("")
        setStatus("idle")
      }, 900)
    }, 1200)
  }

  return (
    <div className="flex max-w-full min-w-0 flex-col items-center gap-3">
      <Label htmlFor="input-otp-demo">Verification code</Label>
      <InputOTP
        id="input-otp-demo"
        length={6}
        variant="separate"
        animated
        status={status}
        value={value}
        onValueChange={(next) => {
          setValue(next)
          setStatus("idle")
        }}
        onValueComplete={verify}
        aria-describedby="input-otp-demo-hint"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <p id="input-otp-demo-hint" className="text-sm text-muted-foreground">
        Type or paste 123456 to pass. Anything else fails.
      </p>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/input-otp.json

Bileşeni, HextaUI tema token'larını ve bileşenin bağımlı olduğu tüm HextaUI bileşenlerini ekler.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
<label htmlFor="code">Verification code</label>
<InputOTP id="code" length={6}>
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
  <InputOTPSeparator />
  <InputOTPGroup>
    <InputOTPSlot />
    <InputOTPSlot />
    <InputOTPSlot />
  </InputOTPGroup>
</InputOTP>

Her karakter için bir <InputOTPSlot /> render edin ve length değerini aynı sayıya ayarlayın. Slotlar konumlarını kendileri bulur; bu yüzden senkron tutulacak bir index prop'u yoktur.

InputOTP
├── InputOTPGroup
│   └── InputOTPSlot
├── InputOTPSeparator
└── InputOTPGroup
    └── InputOTPSlot

Birleşik

Varsayılan görünüm. Her <InputOTPGroup /> slotlarını paylaşılan kenarlarla tek bir şeritte birleştirir.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
import { Label } from "@/components/ui/label"

export function InputOTPBasic() {
  return (
    <div className="flex max-w-full min-w-0 flex-col items-start gap-2">
      <Label htmlFor="input-otp-basic">Verification code</Label>
      <InputOTP id="input-otp-basic" length={6}>
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
    </div>
  )
}

Ayrı

variant="separate" her slota aralarında boşluk olan kendi yuvarlak kutusunu verir.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPSeparate() {
  return (
    <InputOTP length={6} variant="separate" aria-label="Verification code">
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Boyutlar

sm, default ve lg girdi ve düğme yükseklikleriyle eşleşir. Dokunmatik ekranlarda her boyut 16px yazı tipiyle en az 44px'e büyür.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPSizes() {
  return (
    <div className="flex max-w-full min-w-0 flex-col items-start gap-4">
      <InputOTP length={4} size="sm" aria-label="Small code">
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <InputOTP length={4} aria-label="Default code">
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <InputOTP length={4} size="lg" aria-label="Large code">
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
    </div>
  )
}

Animasyonlu

animated varsayılan olarak kapalıdır. Açıkken yazılan karakterler yükselerek girer, silinenler kalanlar kayarken batarak çıkar ve otomatik doldurma, yapıştırma veya kendi state'inizden gelen tam bir kod slot slot basamaklanarak gelir. Basamaklamayı görmek için Fill code'a basın.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPAnimated() {
  const [value, setValue] = React.useState("")

  return (
    <div className="flex max-w-full min-w-0 flex-col items-center gap-4">
      <InputOTP
        length={6}
        variant="separate"
        animated
        value={value}
        onValueChange={setValue}
        aria-label="Verification code"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <div className="flex gap-2">
        <Button variant="outline" size="sm" onClick={() => setValue("482913")}>
          Fill code
        </Button>
        <Button variant="ghost" size="sm" onClick={() => setValue("")}>
          Clear
        </Button>
      </div>
    </div>
  )
}

Durum

status kodu denetlemenin sonucunu gösterir. loading slotları kilitler ve alanı meşgul işaretler, error her slotu geçersiz işaretler ve success kenarları yeşile çevirir. Her biri duyurulur. animated ile loading bir dalga çalıştırır, error bir kez sarsılır ve success karakterleri pop ettirir.

"use client"

import * as React from "react"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
  type InputOTPStatus,
} from "@/components/ui/input-otp"
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"

const statuses: InputOTPStatus[] = ["idle", "loading", "success", "error"]

export function InputOTPStatusExample() {
  const [status, setStatus] = React.useState<InputOTPStatus>("loading")

  return (
    <div className="flex max-w-full min-w-0 flex-col items-center gap-4">
      <InputOTP
        length={6}
        variant="separate"
        animated
        status={status}
        defaultValue="381904"
        aria-label="Verification code"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <ToggleGroup
        aria-label="Status"
        size="sm"
        value={[status]}
        onValueChange={(next) => {
          if (next[0]) {
            setStatus(next[0] as InputOTPStatus)
          }
        }}
      >
        {statuses.map((item) => (
          <ToggleGroupItem key={item} value={item}>
            {item}
          </ToggleGroupItem>
        ))}
      </ToggleGroup>
    </div>
  )
}

Kontrollü

value ve onValueChange geçin. Değer her zaman filtrelenmiş koddur ve asla length'ten uzun olmaz.

"use client"

import * as React from "react"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPControlled() {
  const [value, setValue] = React.useState("")

  return (
    <div className="flex max-w-full min-w-0 flex-col items-center gap-3">
      <InputOTP
        length={6}
        value={value}
        onValueChange={setValue}
        aria-label="Verification code"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <p className="text-sm text-muted-foreground tabular-nums">
        {value === "" ? "Enter your code." : `You entered: ${value}`}
      </p>
    </div>
  )
}

Form

Bir name ile kod formla birlikte gönderilir. autoSubmit son slot dolar dolmaz gönderir; böylece otomatik doldurulan bir kod, kişileri bir dokunuş daha gerekmeden oturum açtırır.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"
import { Label } from "@/components/ui/label"

export function InputOTPForm() {
  const [submitted, setSubmitted] = React.useState<string>()

  return (
    <form
      className="flex max-w-full min-w-0 flex-col items-start gap-3"
      onSubmit={(event) => {
        event.preventDefault()
        setSubmitted(String(new FormData(event.currentTarget).get("code")))
      }}
    >
      <Label htmlFor="input-otp-form">Sign-in code</Label>
      <InputOTP id="input-otp-form" name="code" length={6} required autoSubmit>
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <div className="flex items-center gap-3">
        <Button type="submit" size="sm">
          Continue
        </Button>
        <p role="status" className="text-sm text-muted-foreground tabular-nums">
          {submitted ? `Submitted ${submitted}` : null}
        </p>
      </div>
    </form>
  )
}

Field ile

Bir <Field /> içinde etiket, açıklama ve hata sizin için bağlanır. Hatayı görmek için 000000 dışında bir şey girin.

"use client"

import * as React from "react"

import {
  Field,
  FieldDescription,
  FieldError,
  FieldLabel,
} from "@/components/ui/field"
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPField() {
  const [value, setValue] = React.useState("")
  const [error, setError] = React.useState<string>()

  return (
    <Field invalid={error !== undefined} className="w-fit">
      <FieldLabel>Verification code</FieldLabel>
      <InputOTP
        length={6}
        value={value}
        onValueChange={(next) => {
          setValue(next)
          setError(undefined)
        }}
        onValueComplete={(code) => {
          if (code !== "000000") {
            setError("That code has expired. Request a new one.")
          }
        }}
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <FieldDescription>We sent it to [email protected].</FieldDescription>
      <FieldError errors={error ? [{ message: error }] : []} />
    </Field>
  )
}

Geçersiz

Kökteki aria-invalid her slotu işaretler. Mesajı aria-describedby ile bağlayın.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPInvalid() {
  return (
    <div className="flex max-w-full min-w-0 flex-col items-start gap-2">
      <InputOTP
        length={6}
        defaultValue="111111"
        aria-invalid
        aria-label="Verification code"
        aria-describedby="input-otp-invalid-error"
      >
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
          <InputOTPSlot />
        </InputOTPGroup>
      </InputOTP>
      <p id="input-otp-invalid-error" className="text-sm text-destructive">
        That code doesn’t match. Check the latest message.
      </p>
    </div>
  )
}

Harfler ve sayılar

validationType="alphanumeric" kurtarma ve davet kodlarını kabul eder, normalizeValue ise yazılırken veya yapıştırılırken onları büyük harfe çevirir.

"use client"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPAlphanumeric() {
  return (
    <InputOTP
      length={8}
      validationType="alphanumeric"
      normalizeValue={(value) => value.toUpperCase()}
      aria-label="Recovery code"
    >
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
      <InputOTPSeparator />
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Maskeli

mask PIN'ler için her karakteri gizler. Değer tek kullanımlık bir kod değilse otomatik doldurmayı autoComplete="off" ile kapatın.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPMasked() {
  return (
    <InputOTP
      length={4}
      mask
      animated
      variant="separate"
      autoComplete="off"
      aria-label="PIN"
    >
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Özel ayırıcı

Slotları istediğiniz gibi gruplayın ve <InputOTPSeparator /> bileşenine kendi simgenizi geçin.

import { IconPointFilled } from "@tabler/icons-react"

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPCustomSeparator() {
  return (
    <InputOTP length={6} variant="separate" aria-label="Pairing code">
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
      <InputOTPSeparator>
        <IconPointFilled aria-hidden="true" />
      </InputOTPSeparator>
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
      <InputOTPSeparator>
        <IconPointFilled aria-hidden="true" />
      </InputOTPSeparator>
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Devre dışı

Devre dışı bir alana odaklanılamaz ve düzenlenemez.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSlot,
} from "@/components/ui/input-otp"

export function InputOTPDisabled() {
  return (
    <InputOTP
      length={6}
      defaultValue="12"
      disabled
      aria-label="Verification code"
    >
      <InputOTPGroup>
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
        <InputOTPSlot />
      </InputOTPGroup>
    </InputOTP>
  )
}

Sağdan sola

Slotlar sağdan dolar ve ok tuşları gördüğünüzü izler. İlkinden sonraki slotlara çevrilmiş bir aria-label verin. Sağdan sola bir sayfada kodu soldan sağa tutmak için alanda dir="ltr" ayarlayın.

import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp"
import { Label } from "@/components/ui/label"

export function InputOTPRtl() {
  return (
    <div
      dir="rtl"
      className="flex max-w-full min-w-0 flex-col items-start gap-2"
    >
      <Label htmlFor="input-otp-rtl">رمز التحقق</Label>
      <InputOTP id="input-otp-rtl" length={6} animated>
        <InputOTPGroup>
          <InputOTPSlot />
          <InputOTPSlot aria-label="الخانة ٢ من ٦" />
          <InputOTPSlot aria-label="الخانة ٣ من ٦" />
        </InputOTPGroup>
        <InputOTPSeparator />
        <InputOTPGroup>
          <InputOTPSlot aria-label="الخانة ٤ من ٦" />
          <InputOTPSlot aria-label="الخانة ٥ من ٦" />
          <InputOTPSlot aria-label="الخانة ٦ من ٦" />
        </InputOTPGroup>
      </InputOTP>
    </div>
  )
}
TuşAction
TabOdağı alana, ilk boş slota taşır ve tekrar dışarı çıkarır. Tab sırasında yalnızca bir slot vardır.
←→Önceki veya sonraki slota geçer; sağdan sola düzenlerde görsel sırayla.
Home↑İlk slota geçer.
End↓Son karakterden sonraki slota geçer.
BackspaceSlottaki karakteri siler; slot boşsa öncekini siler. Sonraki karakterler geri kayar.
DeleteSlottaki karakteri siler ve odağı orada tutar.
CtrlBackspaceKodun tamamını temizler. macOS'ta ⌘ Backspace.
CtrlAKodun tamamını seçer (macOS'ta ⌘ A). Backspace veya Delete sonra onu temizler ve ilk slota döner, yazmak veya yapıştırmak onu değiştirir ve Ctrl C hepsini kopyalar. Başka herhangi bir tuş veya tıklama seçimi sonlandırır.
  • Her slot gerçek bir girdidir. İlki adını <label> bileşeninizden veya aria-label'dan alır; diğerleri "Character 2 of 6" gibi adlandırılır. Çevirmek için bir slotta aria-label geçin.
  • İlk slotta autocomplete="one-time-code" bulunur; böylece iOS ve macOS Mesajlar ve Mail'den kodlar sunar, Android SMS kodları sunar ve parola yöneticileri onu doldurabilir. Tek bir slota düşen tam bir kod hepsine dağıtılır. Animasyonlu basamaklama, WebOTP API'sinden ayarladığınız kodlar dahil her kaynak için çalışır.
  • Bir slot odaktayken kod boşaldığında, örneğin yanlış bir kod temizlendikten sonra, odak ilk slota geri döner; böylece bir sonraki deneme doğru yerden başlar.
  • status ayarlandığında alanın yanındaki gizli bir canlı bölge bunu duyurur. Sözcükleri loadingLabel, successLabel ve errorLabel ile değiştirin.
  • animated ile karakterler ekran okuyuculardan gizli bir katmanda çizilir, girdiler ise gerçek değeri tutar. Azaltılmış harekette karakterler yalnızca solar ve durum dalgası hafif bir nabza dönüşür.

Base UI OTP field üzerine kuruludur. Her Base UI prop'u aktarılır.

PropTürVarsayılan
lengthZorunlu. Slot sayısı; aynı sayıda InputOTPSlot parçası render edin.
number–
variant
"joined" | "separate""joined"
size
"sm" | "default" | "lg""default"
animatedKarakterleri animasyonla girip çıkarın, çok karakterli girdiyi basamaklandırın ve durumu animasyonlayın.
booleanfalse
statusKodu denetlemenin sonucu. Loading slotları salt okunur yapar.
"idle" | "loading" | "success" | "error"–
loadingLabel
string"Verifying code"
successLabel
string"Code verified"
errorLabel
string"Code is incorrect"
value
string–
defaultValue
string–
onValueChange
(value: string, details) => void–
onValueCompleteSon slot dolduğunda çağrılır.
(value: string, details) => void–
onValueInvalidYazılan veya yapıştırılan karakterler reddedildiğinde çağrılır.
(value: string, details) => void–
validationType
"numeric" | "alpha" | "alphanumeric" | "none""numeric"
normalizeValueFiltrelemeden sonra çalışır. İdempotent tutun.
(value: string) => string–
inputModeVarsayılan olarak validationType'tan gelir.
string–
autoComplete
string"one-time-code"
autoSubmit
booleanfalse
mask
booleanfalse
aria-invalidHer slotu geçersiz olarak işaretler.
boolean–
name
string–
form
string–
idİlk slota gider; böylece bir etiketin htmlFor'u ona işaret eder.
string–
disabled
booleanfalse
readOnly
booleanfalse
required
booleanfalse
className
string | (state) => string–
render
ReactElement | (props, state) => ReactElement<div>
ÖznitelikAçıklama
data-slot="input-otp"Kök.
data-variant="joined" | "separate"Geçerli varyant.
data-sizeGeçerli boyut.
data-statusAyarlıysa durum.
data-animatedanimated açıkken bulunur.
data-shakeDurum error olduktan sonra alan sarsılırken bulunur.
data-completeHer slot doluyken bulunur.
data-filledHerhangi bir slot doluyken bulunur.
data-focusedBir slot odaktayken bulunur.
data-disabledDevre dışıyken bulunur.
data-readonlyYükleme dahil salt okunur olduğunda bulunur.
data-requiredZorunlu olduğunda bulunur.
data-invalid / data-valid / data-touched / data-dirtyBir Field içinde alan state'i.
data-slot="input-otp-status"Kökün kardeşi olan gizli canlı bölge.

Bir dizi slotu düzenleyen düz bir öğe. Birleşik variant'ta slotlar kenarları paylaşır.

PropTürVarsayılan
render
ReactElement | (props, state) => ReactElement<div>
ÖznitelikAçıklama
data-slot="input-otp-group"CSS'te grubu hedefleyin.

Tek bir girdi tutan bir kutu. className kutuya gider; diğer tüm prop'lar girdiye gider.

PropTürVarsayılan
aria-labelEtiketi kullanan ilk slotta yok sayılır.
string"Character N of M"
classNameState slotun dizinini, değerini, filled'ı ve alan durumunu içerir.
string | (state) => string–
placeholder
string–
ÖznitelikAçıklama
data-slot="input-otp-slot"Kutu.
data-filledSlotta bir karakter olduğunda bulunur.
data-statusBoşta değilse kökün durumu.
--input-otp-indexDurum hareketini kademelendirmek için kullanılan slot konumu.
data-slot="input-otp-input"İçindeki girdi; Base UI'ın data-filled, data-focused, data-complete ve field nitelikleriyle.
data-slot="input-otp-char"animated olduğunda çizilen karakter.

Eksi simgeli bir ayırıcı. Başka bir simge kullanmak için alt öğe geçin.

PropTürVarsayılan
orientation
"horizontal" | "vertical""horizontal"
className
string | (state) => string–
render
ReactElement | (props, state) => ReactElement<div>
ÖznitelikAçıklama
data-slot="input-otp-separator"CSS'te ayırıcıyı hedefleyin.

Bir slotun ve bir grubun arkasındaki sınıf adları (inputOTPGroupVariants). Bunları { variant, size } ile çağırın.

Kullanıldığı bloklar

Input OTP üzerine kurulan bloklar.