{
  "name": "number-field",
  "type": "registry:ui",
  "title": "Number Field",
  "description": "Numeric input with keyboard entry, step controls, range constraints, formatting and optional scrubbing.",
  "registryDependencies": [
    "@siteplane/base",
    "@siteplane/label"
  ],
  "dependencies": [
    "@base-ui/react@1.5.0",
    "lucide-react@1.18.0"
  ],
  "files": [
    {
      "path": "src/components/ui/number-field.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { NumberField as NumberFieldPrimitive } from \"@base-ui/react/number-field\";\nimport { MinusIcon, PlusIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { useResolvedControlSize } from \"@/components/ui/siteplane-provider\";\nimport { Label } from \"@/components/ui/label\";\nimport { surfaceClassNames } from \"@/components/ui/surface\";\n\nexport const NumberFieldContext: React.Context<{\n  fieldId: string;\n} | null> = React.createContext<{\n  fieldId: string;\n} | null>(null);\n\nexport function NumberField({\n  id,\n  className,\n  size,\n  ...props\n}: NumberFieldPrimitive.Root.Props & {\n  size?: \"sm\" | \"default\" | \"lg\" | \"xl\";\n}): React.ReactElement {\n  const generatedId = React.useId();\n  const fieldId = id ?? generatedId;\n  const resolvedSize = useResolvedControlSize(size);\n\n  return (\n    <NumberFieldContext.Provider value={{ fieldId }}>\n      <NumberFieldPrimitive.Root\n        className={cn(\"flex w-full flex-col items-start gap-2\", className)}\n        data-size={resolvedSize}\n        data-slot=\"number-field\"\n        id={fieldId}\n        {...props}\n      />\n    </NumberFieldContext.Provider>\n  );\n}\n\nexport function NumberFieldGroup({\n  className,\n  ...props\n}: NumberFieldPrimitive.Group.Props): React.ReactElement {\n  return (\n    <NumberFieldPrimitive.Group\n      className={cn(\n        surfaceClassNames.formControlFocusWithin,\n        \"justify-between in-[[data-slot=number-field][data-size=xl]]:text-base sm:in-[[data-slot=number-field][data-size=xl]]:text-base has-autofill:bg-foreground/4 dark:has-autofill:bg-foreground/8 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n        className,\n      )}\n      data-slot=\"number-field-group\"\n      {...props}\n    />\n  );\n}\n\nexport function NumberFieldDecrement({\n  className,\n  ...props\n}: NumberFieldPrimitive.Decrement.Props): React.ReactElement {\n  return (\n    <NumberFieldPrimitive.Decrement\n      className={cn(\n        \"relative flex shrink-0 cursor-pointer items-center justify-center rounded-s-[calc(var(--radius-lg)-1px)] in-data-[size=sm]:px-[calc(--spacing(2.5)-1px)] in-data-[size=xl]:px-[calc(--spacing(3.5)-1px)] px-[calc(--spacing(3)-1px)] transition-colors pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent\",\n        className,\n      )}\n      data-slot=\"number-field-decrement\"\n      {...props}\n    >\n      <MinusIcon />\n    </NumberFieldPrimitive.Decrement>\n  );\n}\n\nexport function NumberFieldIncrement({\n  className,\n  ...props\n}: NumberFieldPrimitive.Increment.Props): React.ReactElement {\n  return (\n    <NumberFieldPrimitive.Increment\n      className={cn(\n        \"relative flex shrink-0 cursor-pointer items-center justify-center rounded-e-[calc(var(--radius-lg)-1px)] in-data-[size=sm]:px-[calc(--spacing(2.5)-1px)] in-data-[size=xl]:px-[calc(--spacing(3.5)-1px)] px-[calc(--spacing(3)-1px)] transition-colors pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent\",\n        className,\n      )}\n      data-slot=\"number-field-increment\"\n      {...props}\n    >\n      <PlusIcon />\n    </NumberFieldPrimitive.Increment>\n  );\n}\n\nexport function NumberFieldInput({\n  className,\n  ...props\n}: NumberFieldPrimitive.Input.Props): React.ReactElement {\n  return (\n    <NumberFieldPrimitive.Input\n      className={cn(\n        \"h-8.5 in-data-[size=lg]:h-9.5 in-data-[size=xl]:h-13.5 in-data-[size=sm]:h-7.5 w-full min-w-0 grow bg-transparent in-data-[size=sm]:px-[calc(--spacing(2.5)-1px)] in-data-[size=xl]:px-[calc(--spacing(4)-1px)] px-[calc(--spacing(3)-1px)] text-center tabular-nums in-data-[size=lg]:leading-9.5 in-data-[size=xl]:leading-6 in-data-[size=sm]:leading-7.5 leading-8.5 outline-none [transition:background-color_5000000s_ease-in-out_0s] sm:h-7.5 sm:in-data-[size=lg]:h-8.5 sm:in-data-[size=xl]:h-13.5 sm:in-data-[size=sm]:h-6.5 sm:in-data-[size=lg]:leading-8.5 sm:in-data-[size=xl]:leading-6 sm:in-data-[size=sm]:leading-8.5 sm:leading-7.5\",\n        className,\n      )}\n      data-slot=\"number-field-input\"\n      {...props}\n    />\n  );\n}\n\nexport function NumberFieldScrubArea({\n  className,\n  label,\n  ...props\n}: NumberFieldPrimitive.ScrubArea.Props & {\n  label: string;\n}): React.ReactElement {\n  const context = React.useContext(NumberFieldContext);\n\n  if (!context) {\n    throw new Error(\n      \"NumberFieldScrubArea must be used within a NumberField component for accessibility.\",\n    );\n  }\n\n  return (\n    <NumberFieldPrimitive.ScrubArea\n      className={cn(\"flex cursor-ew-resize\", className)}\n      data-slot=\"number-field-scrub-area\"\n      {...props}\n    >\n      <Label className=\"cursor-ew-resize\" htmlFor={context.fieldId}>\n        {label}\n      </Label>\n      <NumberFieldPrimitive.ScrubAreaCursor className=\"drop-shadow-[0_1px_1px_--theme(--color-black/50%)] filter\">\n        <CursorGrowIcon />\n      </NumberFieldPrimitive.ScrubAreaCursor>\n    </NumberFieldPrimitive.ScrubArea>\n  );\n}\n\nexport function CursorGrowIcon(\n  props: React.ComponentProps<\"svg\">,\n): React.ReactElement {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      fill=\"black\"\n      height=\"14\"\n      stroke=\"white\"\n      viewBox=\"0 0 24 14\"\n      width=\"26\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      {...props}\n    >\n      <path d=\"M19.5 5.5L6.49737 5.51844V2L1 6.9999L6.5 12L6.49737 8.5L19.5 8.5V12L25 6.9999L19.5 2V5.5Z\" />\n    </svg>\n  );\n}\n\nexport { NumberFieldPrimitive };\n"
    }
  ],
  "categories": [
    "form-controls"
  ],
  "docs": "# Number Field\n\n> Siteplane UI release `0.1.1`.\n\n## Public Purpose\n\nUse Number Field for numeric values that benefit from keyboard entry, step buttons, optional scrubbing and explicit range or formatting rules.\n\n## Import\n\n`import { NumberField } from \"@/components/ui/number-field\";`\n\n`import { CursorGrowIcon, NumberFieldDecrement, NumberFieldGroup, NumberFieldIncrement, NumberFieldInput, NumberFieldScrubArea } from \"@/components/ui/number-field\";`\n\n`import { Field, FieldDescription, FieldError, FieldLabel } from \"@/components/ui/field\";`\n\n`import { Form } from \"@/components/ui/form\";`\n\n`import { Label } from \"@/components/ui/label\";`\n\n## Public API\n\n- `NumberField` owns the value and accepts the Base UI number-field props plus `size=\"sm\" | \"default\" | \"lg\" | \"xl\"`.\n- `size=\"xl\"` gives the composed group the shared 56px customer-facing outer\n  height and 16px value text. Use the surrounding provider's `radius=\"sm\"`\n  personality for the global 8px main corner.\n- Compose `NumberFieldGroup`, `NumberFieldInput`, `NumberFieldDecrement` and `NumberFieldIncrement` for the standard control.\n- `NumberFieldScrubArea` adds a labelled drag target for changing the value; `CursorGrowIcon` is the matching visual indicator.\n- Configure `min`, `max`, `step`, `format`, controlled `value` or uncontrolled `defaultValue` on `NumberField`, not on the input slot.\n\n## Public Motion\n\n- Number Field adds no root-level enter or exit animation. Button feedback, focus, disabled and invalid states come from the native Siteplane primitives.\n- Scrubbing updates the value directly without moving the surrounding layout. The optional Rolling Digits documentation example is not part of the default primitive and reduces its digit transition to one frame in system and provider reduced-motion modes.\n\n## Public Accessibility\n\n- Provide a visible label through `FieldLabel` or an explicit accessible name.\n- Give `NumberFieldScrubArea` a label that describes the value it changes.\n- Keep increment, decrement and keyboard behavior on the Base UI-backed slots, and pair invalid state with a visible `FieldError`.\n\n## Public Agent Guidance\n\n- Use Input for free-form text and Slider for visual range selection.\n- Do not rebuild a numeric input from raw browser controls when Number Field fits.\n- Keep range, step and formatting rules on the root so every input method shares one contract.\n\n## Public Links\n\n- [Documentation and preview](https://ui.siteplane.io/docs/components/form-controls/number-field)\n\n- [Registry source](https://ui.siteplane.io/r/number-field.json)\n",
  "meta": {
    "siteplane:agentContractVersion": 1,
    "siteplane:docs": "https://ui.siteplane.io/docs/components/form-controls/number-field",
    "siteplane:releaseVersion": "0.1.1",
    "siteplane:sourceOwned": true
  },
  "devDependencies": []
}
