{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input-group",
  "title": "Input Group",
  "author": "Ballmac <https://ui.ballmac.com>",
  "description": "One bordered field that wraps an input or textarea with icon, text, button and keyboard-hint addons at either end or above and below.",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ballmac/components/input-group.tsx",
      "content": "// Ballmac UI: Input Group. https://ui.ballmac.com/components/input-group\n// Based on shadcn/ui Input Group (MIT, Copyright (c) 2023 shadcn), adding a size scale, invalid state, and click-to-focus addons.\n\"use client\";\n\nimport * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { cn } from \"@/lib/utils\";\n\ntype InputGroupProps = React.ComponentProps<\"div\"> & {\n  /** Accessible name for the group, for example \"Website address\". */\n  \"aria-label\"?: string;\n};\n/**\n * A bordered field that holds one input or textarea plus addons: icons, text, buttons, keyboard hints.\n * The group draws the border, focus ring and invalid state; the control inside is borderless.\n */\nfunction InputGroup({ className, ...props }: InputGroupProps) {\n  return (\n    <div\n      data-slot=\"input-group\"\n      role=\"group\"\n      className={cn(\n        \"group/input-group relative flex h-9 w-full min-w-0 items-center rounded-md border border-input bg-background shadow-xs transition-[color,border-color,box-shadow] duration-150 dark:bg-input/30\",\n        \"has-[>textarea]:h-auto\",\n        \"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col\",\n        \"has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50\",\n        \"has-[[data-slot=input-group-control][aria-invalid=true]]:border-destructive has-[[data-slot=input-group-control][aria-invalid=true]]:ring-destructive/20\",\n        \"has-[[data-slot=input-group-control]:disabled]:opacity-50\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nconst addonVariants = cva(\n  \"flex cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-sm\",\n  {\n    variants: {\n      align: {\n        \"inline-start\": \"order-first ps-3 has-[>button]:-ms-1.5\",\n        \"inline-end\": \"order-last pe-3 has-[>button]:-me-1.5\",\n        \"block-start\": \"order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5\",\n        \"block-end\": \"order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5\",\n      },\n    },\n    defaultVariants: { align: \"inline-start\" },\n  },\n);\n\ntype InputGroupAddonProps = React.ComponentProps<\"div\"> &\n  VariantProps<typeof addonVariants> & {\n    /** Where the addon sits. Inline addons flank the control; block addons stack above or below it (for textareas). */\n    align?: \"inline-start\" | \"inline-end\" | \"block-start\" | \"block-end\";\n  };\nfunction InputGroupAddon({\n  className,\n  align = \"inline-start\",\n  onClick,\n  ...props\n}: InputGroupAddonProps) {\n  return (\n    <div\n      data-slot=\"input-group-addon\"\n      data-align={align}\n      className={cn(addonVariants({ align }), className)}\n      onClick={(event) => {\n        onClick?.(event);\n        if (event.defaultPrevented || (event.target as HTMLElement).closest(\"button, a\")) return;\n        event.currentTarget.parentElement\n          ?.querySelector<HTMLElement>(\"[data-slot=input-group-control]\")\n          ?.focus();\n      }}\n      {...props}\n    />\n  );\n}\n\nconst buttonVariants = cva(\"flex items-center gap-2 text-sm shadow-none\", {\n  variants: {\n    size: {\n      xs: \"h-6 gap-1 rounded-[5px] px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5\",\n      sm: \"h-7 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5\",\n      \"icon-xs\": \"size-6 rounded-[5px] p-0\",\n      \"icon-sm\": \"size-7 rounded-md p-0\",\n    },\n    variant: {\n      ghost: \"text-muted-foreground hover:bg-accent hover:text-accent-foreground\",\n      secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n      default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n    },\n  },\n  defaultVariants: { size: \"xs\", variant: \"ghost\" },\n});\n\ntype InputGroupButtonProps = Omit<React.ComponentProps<\"button\">, \"size\"> &\n  VariantProps<typeof buttonVariants>;\nfunction InputGroupButton({\n  className,\n  type = \"button\",\n  variant = \"ghost\",\n  size = \"xs\",\n  ...props\n}: InputGroupButtonProps) {\n  return (\n    <button\n      type={type}\n      data-slot=\"input-group-button\"\n      data-size={size}\n      className={cn(\n        buttonVariants({ variant, size }),\n        \"inline-flex shrink-0 items-center justify-center font-medium whitespace-nowrap outline-none transition-colors focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\ntype InputGroupTextProps = React.ComponentProps<\"span\">;\nfunction InputGroupText({ className, ...props }: InputGroupTextProps) {\n  return (\n    <span\n      data-slot=\"input-group-text\"\n      className={cn(\n        \"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\ntype InputGroupInputProps = React.ComponentProps<\"input\">;\nfunction InputGroupInput({ className, ...props }: InputGroupInputProps) {\n  return (\n    <input\n      data-slot=\"input-group-control\"\n      className={cn(\n        \"h-full min-w-0 flex-1 bg-transparent px-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed\",\n        \"group-has-[[data-align=inline-start]]/input-group:ps-1.5 group-has-[[data-align=inline-end]]/input-group:pe-1.5\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\ntype InputGroupTextareaProps = React.ComponentProps<\"textarea\">;\nfunction InputGroupTextarea({ className, ...props }: InputGroupTextareaProps) {\n  return (\n    <textarea\n      data-slot=\"input-group-control\"\n      className={cn(\n        \"min-h-16 w-full min-w-0 flex-1 resize-none bg-transparent px-3 py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupText,\n  InputGroupInput,\n  InputGroupTextarea,\n  type InputGroupProps,\n  type InputGroupAddonProps,\n  type InputGroupButtonProps,\n  type InputGroupTextProps,\n  type InputGroupInputProps,\n  type InputGroupTextareaProps,\n};\n",
      "type": "registry:ui",
      "target": "@components/ballmac/input-group.tsx"
    }
  ],
  "meta": {
    "schema": 1,
    "tier": "free",
    "tags": [
      "input",
      "addon",
      "form",
      "textarea"
    ],
    "version": "1.0.0",
    "updated": "2026-09-30",
    "ai": {
      "summary": "Put InputGroupInput or InputGroupTextarea plus InputGroupAddon parts inside InputGroup. The group draws the border, focus ring and invalid state.",
      "whenToUse": [
        "URL and currency prefixes and suffixes",
        "Search fields with a button",
        "Chat composers with a send button"
      ],
      "whenNotToUse": [
        "A plain field; use input",
        "Attached separate buttons; use button-group"
      ],
      "composesWith": [
        "field",
        "button",
        "kbd"
      ],
      "a11y": [
        {
          "keys": "Click addon text",
          "action": "Focuses the control"
        },
        {
          "keys": "Tab",
          "action": "Moves between the control and addon buttons"
        }
      ],
      "customization": [
        "addon align: inline-start | inline-end | block-start | block-end",
        "button size: xs | sm | icon-xs | icon-sm",
        "aria-invalid on the control"
      ]
    },
    "source": {
      "name": "shadcn/ui Input Group",
      "url": "https://github.com/shadcn-ui/ui",
      "license": "MIT",
      "copyright": "Copyright (c) 2023 shadcn",
      "modified": true
    },
    "examples": [
      "input-group-demo",
      "input-group-states"
    ],
    "url": "https://ui.ballmac.com/components/input-group"
  },
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}