{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input-spinner",
  "title": "Input Spinner",
  "description": "A minimalist input spinner for choosing quantities with buttons, typing, or arrow keys.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "components/input/InputSpinner.tsx",
      "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport type { ChangeEvent, InputHTMLAttributes } from \"react\";\nimport { Minus, Plus } from \"lucide-react\";\n\ntype InputSpinnerProps = Omit<\n  InputHTMLAttributes<HTMLInputElement>,\n  \"type\" | \"value\" | \"defaultValue\" | \"onChange\" | \"min\" | \"max\" | \"step\"\n> & {\n  value?: number;\n  defaultValue?: number;\n  min?: number;\n  max?: number;\n  step?: number;\n  // eslint-disable-next-line no-unused-vars\n  onValueChange?: (value: number) => void;\n  className?: string;\n};\n\nexport default function InputSpinner({\n  value,\n  defaultValue = 0,\n  min,\n  max,\n  step = 1,\n  onValueChange,\n  className = \"\",\n  disabled,\n  ...props\n}: InputSpinnerProps) {\n  const [internalValue, setInternalValue] = useState(String(defaultValue));\n  const displayValue = value === undefined ? internalValue : String(value);\n  const numericValue = Number(displayValue);\n\n  const updateValue = (nextValue: number) => {\n    const clampedValue = Math.min(\n      max ?? Infinity,\n      Math.max(min ?? -Infinity, nextValue),\n    );\n    const roundedValue = Number(clampedValue.toFixed(10));\n\n    if (value === undefined) setInternalValue(String(roundedValue));\n    onValueChange?.(roundedValue);\n  };\n\n  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {\n    const nextValue = event.target.value;\n    if (value === undefined) setInternalValue(nextValue);\n    if (nextValue !== \"\") onValueChange?.(event.target.valueAsNumber);\n  };\n\n  const stepValue = (direction: -1 | 1) => {\n    const startingValue = Number.isNaN(numericValue)\n      ? (min ?? 0)\n      : numericValue;\n    updateValue(startingValue + direction * Number(step));\n  };\n\n  return (\n    <div\n      className={[\n        \"inline-flex h-12 overflow-hidden rounded-[var(--radius-xl,0.75rem)] border border-[color:var(--color-border,#e5e7eb)] bg-[var(--color-background,#fff)] shadow-sm transition focus-within:border-[color:var(--color-input,#9ca3af)] focus-within:ring-4 focus-within:ring-[var(--color-ring,#f3f4f6)]\",\n        disabled && \"opacity-60\",\n        className,\n      ]\n        .filter(Boolean)\n        .join(\" \")}\n    >\n      <button\n        type=\"button\"\n        aria-label=\"Decrease value\"\n        onClick={() => stepValue(-1)}\n        disabled={disabled || (min !== undefined && numericValue <= min)}\n        className=\"flex w-11 items-center justify-center border-r border-[color:var(--color-border,#f3f4f6)] text-[color:var(--color-muted-foreground,#6b7280)] transition hover:bg-[var(--color-muted,#f9fafb)] hover:text-[color:var(--color-foreground,#030712)] focus:z-10 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[color:var(--color-ring,#6b7280)] disabled:cursor-not-allowed disabled:text-[color:var(--color-muted-foreground,#d1d5db)]\"\n      >\n        <Minus className=\"h-4 w-4\" strokeWidth={3} aria-hidden=\"true\" />\n      </button>\n\n      <input\n        {...props}\n        type=\"number\"\n        value={displayValue}\n        min={min}\n        max={max}\n        step={step}\n        disabled={disabled}\n        onChange={handleChange}\n        className=\"w-16 appearance-none bg-transparent px-2 text-center text-sm font-semibold tabular-nums text-[color:var(--color-foreground,#030712)] outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none\"\n      />\n\n      <button\n        type=\"button\"\n        aria-label=\"Increase value\"\n        onClick={() => stepValue(1)}\n        disabled={disabled || (max !== undefined && numericValue >= max)}\n        className=\"flex w-11 items-center justify-center border-l border-[color:var(--color-border,#f3f4f6)] text-[color:var(--color-muted-foreground,#6b7280)] transition hover:bg-[var(--color-muted,#f9fafb)] hover:text-[color:var(--color-foreground,#030712)] focus:z-10 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[color:var(--color-ring,#6b7280)] disabled:cursor-not-allowed disabled:text-[color:var(--color-muted-foreground,#d1d5db)]\"\n      >\n        <Plus className=\"h-4 w-4\" strokeWidth={3} aria-hidden=\"true\" />\n      </button>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/input/InputSpinner.tsx"
    }
  ],
  "categories": [
    "input"
  ],
  "type": "registry:component"
}