{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input-group",
  "title": "Input group",
  "author": "Tori Bryan",
  "description": "An input with an icon, text or button beside it, drawn as one field.",
  "dependencies": [
    "@base-ui/react",
    "class-variance-authority"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/input-group.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Input as InputPrimitive } from \"@base-ui/react/input\"\nimport { mergeProps } from \"@base-ui/react/merge-props\"\nimport { useRender } from \"@base-ui/react/use-render\"\nimport { cva } from \"class-variance-authority\"\n\nimport { useFieldSize, type FieldSize } from \"@/lib/field-size\"\nimport { cn } from \"@/lib/utils\"\n\nconst inputGroupVariants = cva(\n  \"group/input-group relative flex w-full min-w-0 items-center transition-colors has-[[data-slot=input-group-control]:disabled]:opacity-50\",\n  {\n    variants: {\n      variant: {\n        default:\n          \"rounded-sm border border-input bg-input-subtle 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-subtle has-[[data-slot=input-group-control][aria-invalid=true]]:border-destructive has-[[data-slot=input-group-control][aria-invalid=true]]:ring-[3px] has-[[data-slot=input-group-control][aria-invalid=true]]:ring-destructive-ring\",\n        // For a search at the top of a menu or dialog, where the surface\n        // around it is the field.\n        ghost: \"\",\n      },\n      size: {\n        sm: \"h-8\",\n        default: \"h-9\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  }\n)\n\nfunction InputGroup({\n  variant = \"default\",\n  size: sizeProp,\n  className,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  /** `ghost` drops the border, fill and ring, for a search at the top of a menu or dialog. */\n  variant?: \"default\" | \"ghost\"\n  /** Height of the group. Defaults to the size of the Field or FieldGroup around it. */\n  size?: FieldSize\n}) {\n  const size = useFieldSize(sizeProp)\n  return (\n    <div\n      data-slot=\"input-group\"\n      data-variant={variant}\n      data-size={size}\n      role=\"group\"\n      className={cn(inputGroupVariants({ variant, size }), className)}\n      {...props}\n    />\n  )\n}\n\nconst inputGroupAddonVariants = cva(\n  \"flex h-full shrink-0 cursor-text items-center gap-2 text-sm text-muted-foreground select-none [&>svg:not([class*='size-'])]:size-4\",\n  {\n    variants: {\n      align: {\n        \"inline-start\": \"order-first pl-3 has-[>button]:pl-1\",\n        \"inline-end\": \"order-last pr-3 has-[>button]:pr-1\",\n      },\n    },\n    defaultVariants: {\n      align: \"inline-start\",\n    },\n  }\n)\n\n/** An icon, text or button beside the input. Pressing anywhere but a button in it focuses the input. */\nfunction InputGroupAddon({\n  align = \"inline-start\",\n  className,\n  onMouseDown,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  /** Which side of the input the addon sits on. */\n  align?: \"inline-start\" | \"inline-end\"\n}) {\n  return (\n    <div\n      data-slot=\"input-group-addon\"\n      data-align={align}\n      className={cn(inputGroupAddonVariants({ align }), className)}\n      onMouseDown={(event) => {\n        onMouseDown?.(event)\n        if ((event.target as Element).closest(\"button, a, input\")) return\n        // The parent rather than a data-slot lookup, since parts such as\n        // Search input and Data table give the group their own slot.\n        const control =\n          event.currentTarget.parentElement?.querySelector<HTMLElement>(\n            \"[data-slot=input-group-control]\"\n          )\n        if (!control) return\n        // Keeps the caret where it was instead of letting the press blur it.\n        event.preventDefault()\n        control.focus()\n      }}\n      {...props}\n    />\n  )\n}\n\n/**\n * The input, borderless so the group draws the field. It is Base UI's\n * Input, so a Field around the group labels and describes it; `render`\n * swaps in another input, such as an Autocomplete's, and keeps the look.\n */\nfunction InputGroupInput({\n  className,\n  render,\n  ref,\n  ...props\n}: useRender.ComponentProps<\"input\">) {\n  return useRender({\n    render: render ?? <InputPrimitive />,\n    ref,\n    props: mergeProps<\"input\">(\n      {\n        \"data-slot\": \"input-group-control\",\n        className: cn(\n          \"h-full w-full min-w-0 flex-1 bg-transparent px-3 text-base outline-hidden group-has-[[data-align=inline-end]]/input-group:pr-2 group-has-[[data-align=inline-start]]/input-group:pl-2 placeholder:text-muted-foreground disabled:cursor-not-allowed md:text-sm\",\n          className\n        ),\n      } as React.ComponentProps<\"input\">,\n      props\n    ),\n  })\n}\n\nexport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n  inputGroupAddonVariants,\n  inputGroupVariants,\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/lib/field-size.ts",
      "content": "import * as React from \"react\"\n\nexport type FieldSize = \"sm\" | \"default\"\n\n/**\n * The size a FieldGroup or Field sets for the controls inside it. It lives\n * here rather than in the Field part so a control can read it without\n * installing Field.\n */\nexport const FieldSizeContext = React.createContext<FieldSize | undefined>(\n  undefined\n)\n\n/** A control's own size wins, then the nearest Field's or FieldGroup's. */\nexport function useFieldSize(size?: FieldSize): FieldSize {\n  const inherited = React.useContext(FieldSizeContext)\n  return size ?? inherited ?? \"default\"\n}\n",
      "type": "registry:lib"
    }
  ],
  "cssVars": {
    "theme": {
      "color-destructive-ring": "var(--destructive-ring)",
      "color-input-subtle": "var(--input-subtle)",
      "color-ring-subtle": "var(--ring-subtle)",
      "border-color-destructive": "var(--destructive-border)"
    },
    "light": {
      "destructive-ring": "color-mix(in oklch, var(--color-red-700) 20%, transparent)",
      "input-subtle": "color-mix(in oklch, var(--color-neutral-200) 20%, transparent)",
      "ring-subtle": "color-mix(in oklch, var(--color-neutral-400) 50%, transparent)",
      "destructive-border": "var(--destructive)"
    },
    "dark": {
      "destructive-ring": "color-mix(in oklch, var(--color-red-400) 40%, transparent)",
      "input-subtle": "oklch(1 0 0 / 5%)",
      "ring-subtle": "color-mix(in oklch, var(--color-neutral-500) 50%, transparent)",
      "destructive-border": "var(--destructive)"
    }
  },
  "meta": {
    "tier": "base-components",
    "group": "Forms",
    "docs": "https://fibo.toribryan.com/?path=/docs/base-components-forms-input-group--docs"
  },
  "docs": "Docs and live examples: https://fibo.toribryan.com/?path=/docs/base-components-forms-input-group--docs",
  "categories": [
    "base-components",
    "forms"
  ],
  "type": "registry:ui"
}