{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pin-input",
  "title": "PIN Input",
  "description": "A six-digit PIN input with paste support and keyboard navigation for verification codes.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "components/input/PinInput.tsx",
      "content": "\"use client\";\n\nimport { useRef, useState } from \"react\";\n\ntype PinInputProps = {\n  value?: string;\n  defaultValue?: string;\n  length?: number;\n  disabled?: boolean;\n  autoFocus?: boolean;\n  // eslint-disable-next-line no-unused-vars\n  onValueChange?: (value: string) => void;\n  className?: string;\n  \"aria-label\"?: string;\n};\n\nconst digitsOnly = (value: string, length: number) =>\n  value.replace(/\\D/g, \"\").slice(0, length);\n\nexport default function PinInput({\n  value,\n  defaultValue = \"\",\n  length = 6,\n  disabled,\n  autoFocus,\n  onValueChange,\n  className = \"\",\n  \"aria-label\": ariaLabel = \"Verification code\",\n}: PinInputProps) {\n  const [internalValue, setInternalValue] = useState(() =>\n    digitsOnly(defaultValue, length)\n  );\n  const inputs = useRef<Array<HTMLInputElement | null>>([]);\n  const pin = digitsOnly(value ?? internalValue, length);\n\n  const updateValue = (nextValue: string) => {\n    const nextPin = digitsOnly(nextValue, length);\n    if (value === undefined) setInternalValue(nextPin);\n    onValueChange?.(nextPin);\n  };\n\n  const fillFrom = (index: number, nextValue: string) => {\n    const digits = digitsOnly(nextValue, length);\n    if (!digits) {\n      updateValue(pin.slice(0, index) + pin.slice(index + 1));\n      return;\n    }\n    const nextPin = [...pin];\n\n    for (const [offset, digit] of [...digits].entries()) {\n      if (index + offset === length) break;\n      nextPin[index + offset] = digit;\n    }\n\n    updateValue(nextPin.join(\"\"));\n    inputs.current[Math.min(index + digits.length, length - 1)]?.focus();\n  };\n\n  return (\n    <div\n      role=\"group\"\n      aria-label={ariaLabel}\n      className={[\"flex items-center gap-2\", className].filter(Boolean).join(\" \")}\n      onPaste={(event) => {\n        event.preventDefault();\n        const index = Number((event.target as HTMLInputElement).dataset.index);\n        fillFrom(index, event.clipboardData.getData(\"text\"));\n      }}\n    >\n      {Array.from({ length }, (_, index) => (\n        <input\n          key={index}\n          ref={(element) => {\n            inputs.current[index] = element;\n          }}\n          data-index={index}\n          type=\"text\"\n          inputMode=\"numeric\"\n          autoComplete={index === 0 ? \"one-time-code\" : \"off\"}\n          autoFocus={autoFocus && index === 0}\n          aria-label={`Digit ${index + 1} of ${length}`}\n          value={pin[index] ?? \"\"}\n          disabled={disabled}\n          maxLength={length - index}\n          onChange={(event) => fillFrom(index, event.target.value)}\n          onKeyDown={(event) => {\n            if (event.key === \"Backspace\" && !pin[index] && index > 0) {\n              event.preventDefault();\n              updateValue(pin.slice(0, index - 1) + pin.slice(index));\n              inputs.current[index - 1]?.focus();\n            }\n            if (event.key === \"ArrowLeft\" && index > 0) inputs.current[index - 1]?.focus();\n            if (event.key === \"ArrowRight\" && index < length - 1) inputs.current[index + 1]?.focus();\n          }}\n          className=\"h-12 w-11 rounded-[var(--radius-xl,0.75rem)] border border-[color:var(--color-border,#e5e7eb)] bg-[var(--color-background,#fff)] text-center text-lg font-semibold tabular-nums text-[color:var(--color-foreground,#030712)] shadow-sm outline-none transition placeholder:text-[color:var(--color-muted-foreground,#9ca3af)] focus:border-[color:var(--color-input,#030712)] focus:ring-4 focus:ring-[var(--color-ring,#f3f4f6)] disabled:cursor-not-allowed disabled:bg-[var(--color-muted,#f9fafb)] disabled:text-[color:var(--color-muted-foreground,#9ca3af)]\"\n        />\n      ))}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/input/PinInput.tsx"
    }
  ],
  "categories": [
    "input"
  ],
  "type": "registry:component"
}