{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input-otp",
  "type": "registry:ui",
  "title": "Input OTP",
  "description": "One-time-code input with per-digit slots, paste handling, and auto-advance.",
  "dependencies": [
    "@saasflare/ui",
    "input-otp"
  ],
  "files": [
    {
      "path": "components/ui/input-otp.tsx",
      "type": "registry:ui",
      "content": "// @toreview\n\"use client\"\n\n/**\n * @fileoverview InputOTP primitive — one-time password input with individual character slots.\n * Built on the input-otp library. Part of the Saasflare base component layer.\n * @module packages/ui/components/ui/input-otp\n * @layer core\n *\n * @component\n * @example\n * import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from '@saasflare/ui';\n * <InputOTP maxLength={6}>\n *   <InputOTPGroup>\n *     <InputOTPSlot index={0} />\n *     <InputOTPSlot index={1} />\n *     <InputOTPSlot index={2} />\n *   </InputOTPGroup>\n *   <InputOTPSeparator />\n *   <InputOTPGroup>\n *     <InputOTPSlot index={3} />\n *     <InputOTPSlot index={4} />\n *     <InputOTPSlot index={5} />\n *   </InputOTPGroup>\n * </InputOTP>\n */\n\nimport * as React from \"react\"\nimport { OTPInput, OTPInputContext } from \"input-otp\"\nimport { MinusIcon } from \"@saasflare/ui\"\n\nimport { cn } from \"@saasflare/ui\"\nimport { useSaasflareProps, type SaasflareComponentProps } from \"@saasflare/ui\"\n\n/**\n * Props for {@link InputOTP}.\n *\n * Extends the `input-otp` OTPInput props with {@link SaasflareComponentProps}.\n * `containerClassName` styles the outer flex wrapper; `className` styles the\n * underlying input element.\n */\ntype InputOTPProps = React.ComponentProps<typeof OTPInput> & SaasflareComponentProps & {\n  containerClassName?: string\n}\n\n/**\n * Root one-time-password input. Owns focus management and the hidden input\n * via the `input-otp` library; resolves the Saasflare contract axes and emits\n * `data-surface`/`data-radius`/`data-animated` for downstream theming.\n */\nfunction InputOTP({\n  className,\n  containerClassName,\n  surface,\n  radius,\n  animated,\n  iconWeight,\n  ...props\n}: InputOTPProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n\n  return (\n    <OTPInput\n      {...props}\n      data-slot=\"input-otp\"\n      data-surface={sf.surface}\n      data-radius={sf.radius}\n      data-animated={String(sf.animated)}\n      containerClassName={cn(\n        \"flex items-center gap-2 has-disabled:opacity-50\",\n        containerClassName\n      )}\n      className={cn(\"disabled:cursor-not-allowed\", className)}\n    />\n  )\n}\n\n/** Props for {@link InputOTPGroup}. */\ninterface InputOTPGroupProps\n  extends Omit<React.ComponentProps<\"div\">, keyof SaasflareComponentProps>,\n    SaasflareComponentProps {}\n\n/**\n * Visual grouping for a run of {@link InputOTPSlot}s. Resolves the contract\n * axes and emits `data-surface`/`data-radius`/`data-animated` so the group\n * stays consistent with the rest of the OTP family.\n */\nfunction InputOTPGroup({\n  className,\n  surface,\n  radius,\n  animated,\n  iconWeight,\n  ...props\n}: InputOTPGroupProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n  return (\n    <div\n      data-slot=\"input-otp-group\"\n      data-surface={sf.surface}\n      data-radius={sf.radius}\n      data-animated={String(sf.animated)}\n      className={cn(\"flex items-center\", className)}\n      {...props}\n    />\n  )\n}\n\n/** Props for {@link InputOTPSlot}. */\ninterface InputOTPSlotProps\n  extends Omit<React.ComponentProps<\"div\">, keyof SaasflareComponentProps>,\n    SaasflareComponentProps {\n  /** Zero-based position of this slot within the OTP value. */\n  index: number\n}\n\n/**\n * A single visible character box. Renders the bordered/rounded cell plus the\n * blinking fake caret. Resolves the contract axes and emits\n * `data-surface`/`data-radius`/`data-animated` so per-slot theming applies.\n */\nfunction InputOTPSlot({\n  index,\n  className,\n  surface,\n  radius,\n  animated,\n  iconWeight,\n  ...props\n}: InputOTPSlotProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n  const inputOTPContext = React.useContext(OTPInputContext)\n  const { char, hasFakeCaret, isActive } = inputOTPContext?.slots?.[index] ?? {}\n\n  return (\n    <div\n      data-slot=\"input-otp-slot\"\n      data-active={isActive}\n      data-surface={sf.surface}\n      data-radius={sf.radius}\n      data-animated={String(sf.animated)}\n      className={cn(\n        \"relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-[3px] data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40\",\n        className\n      )}\n      {...props}\n    >\n      {char}\n      {hasFakeCaret && (\n        <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n          <div className=\"h-4 w-px animate-caret-blink bg-foreground duration-1000\" />\n        </div>\n      )}\n    </div>\n  )\n}\n\n/** Props for {@link InputOTPSeparator}. */\ninterface InputOTPSeparatorProps\n  extends Omit<React.ComponentProps<\"div\">, keyof SaasflareComponentProps>,\n    SaasflareComponentProps {}\n\n/**\n * Visual divider between OTP groups, rendering a Phosphor `MinusIcon` whose\n * weight follows the resolved `iconWeight` axis.\n */\nfunction InputOTPSeparator({\n  surface,\n  radius,\n  animated,\n  iconWeight,\n  ...props\n}: InputOTPSeparatorProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n  return (\n    <div\n      data-slot=\"input-otp-separator\"\n      data-surface={sf.surface}\n      data-radius={sf.radius}\n      data-animated={String(sf.animated)}\n      role=\"separator\"\n      {...props}\n    >\n      <MinusIcon weight={sf.iconWeight} />\n    </div>\n  )\n}\n\nexport {\n  InputOTP,\n  InputOTPGroup,\n  InputOTPSlot,\n  InputOTPSeparator,\n  type InputOTPProps,\n  type InputOTPGroupProps,\n  type InputOTPSlotProps,\n  type InputOTPSeparatorProps,\n}\n",
      "target": "components/ui/input-otp.tsx"
    }
  ],
  "$meta": {
    "source": "https://ui.saasflare.io/r/input-otp.json"
  }
}
