{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input-group",
  "title": "Input Group",
  "description": "A input-group component.",
  "dependencies": [
    "class-variance-authority"
  ],
  "registryDependencies": [
    "@cubby-ui/button",
    "@cubby-ui/input",
    "@cubby-ui/textarea"
  ],
  "files": [
    {
      "path": "registry/default/input-group/input-group.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/registry/default/button/button\";\nimport { Input, type InputProps } from \"@/registry/default/input/input\";\nimport { Textarea } from \"@/registry/default/textarea/textarea\";\n\nconst inputGroupVariants = cva(\n  [\n    \"group/input-group relative flex w-full min-w-0 items-center rounded-(--input-group-radius) border bg-clip-padding\",\n    \"[--input-group-item-radius:max(0px,calc(var(--input-group-radius)-3px))]\",\n    // A fixed shell, as in Number Field: the input stretches to fill it, and\n    // buttons sit in it with a 2px transparent-border inset.\n    \"h-(--input-group-h) has-[>textarea]:h-auto\",\n\n    // A block addon sits closer to an input.\n    \"[&:has(>input)>[data-align=block-start]]:pt-2.5 [&:has(>input)>[data-align=block-end]]:pb-2.5\",\n\n    // Variants based on alignment. The input checks its siblings to keep\n    // `:has()` cheap.\n    \"[&>input:is(:has(~[data-align=inline-start]),[data-align=inline-start]~*)]:pl-2\",\n    \"[&>input:is(:has(~[data-align=inline-end]),[data-align=inline-end]~*)]:pr-2\",\n    \"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col [&>input:is(:has(~[data-align=block-start]),[data-align=block-start]~*)]:pb-3\",\n    \"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col [&>input:is(:has(~[data-align=block-end]),[data-align=block-end]~*)]:pt-3\",\n\n    // Focus state.\n    \"has-[[data-slot=input-group-control]:focus-visible]:outline-ring/50 has-[[data-slot=input-group-control]:focus-visible]:outline-2 has-[[data-slot=input-group-control]:focus-visible]:outline-offset-2 has-[[data-slot=input-group-control]:focus-visible]:outline-solid\",\n\n    // Error state.\n    \"has-[[data-slot][aria-invalid=true]]:outline-destructive/50 has-[[data-slot][aria-invalid=true]]:outline-2 has-[[data-slot][aria-invalid=true]]:outline-offset-2 has-[[data-slot][aria-invalid=true]]:outline-solid\",\n\n    // Transition outline\n    \"outline-0 outline-offset-0 outline-transparent transition-[outline-width,outline-offset,outline-color] duration-100 ease-out\",\n  ],\n  {\n    variants: {\n      // Input's heights (one step taller below sm); corners step with them.\n      size: {\n        default:\n          \"[--input-group-h:--spacing(10)] sm:not-pointer-coarse:[--input-group-h:--spacing(9)] [--input-group-radius:var(--radius)]\",\n        sm: \"[--input-group-h:--spacing(9)] sm:not-pointer-coarse:[--input-group-h:--spacing(8)] [--input-group-radius:calc(var(--radius)-2px)]\",\n      },\n      variant: {\n        // Opaque \"lifted\" bg — use on the page or any non-elevated substrate.\n        default: \"bg-input\",\n        // Translucent overlay — use inside Cards, Dialogs, or any surface\n        // where the opaque default would collapse into its parent.\n        elevated: \"bg-input-elevated\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n      size: \"default\",\n    },\n  },\n);\n\nexport type InputGroupProps = React.ComponentProps<\"div\"> &\n  VariantProps<typeof inputGroupVariants>;\n\nfunction InputGroup({ className, variant, size, ...props }: InputGroupProps) {\n  return (\n    <div\n      data-slot=\"input-group\"\n      data-size={size ?? \"default\"}\n      role=\"group\"\n      className={cn(inputGroupVariants({ variant, size }), className)}\n      {...props}\n    />\n  );\n}\n\nconst inputGroupAddonVariants = cva(\n  [\n    \"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)]\",\n    // Dimmed while the control is disabled. Set on the addon, not the\n    // group, to keep `:has()` cheap.\n    \"[[data-slot=input-group]:has([data-slot=input-group-control]:disabled)>&]:opacity-60\",\n  ],\n  {\n    variants: {\n      // Inline addons fill the shell's height. A button on the outer edge\n      // takes the place of the padding, so its corners are concentric with\n      // the shell's. The edge check skips the hidden spans an open popup\n      // renders beside its trigger and portal (focus guards, aria-owns).\n      align: {\n        \"inline-start\":\n          \"order-first self-stretch pl-3 has-[>button:nth-child(1_of_:not([data-base-ui-focus-guard],[aria-owns]))]:pl-0 has-[>kbd]:pl-2\",\n        \"inline-end\":\n          \"order-last self-stretch pr-3 has-[>button:nth-last-child(1_of_:not([data-base-ui-focus-guard],[aria-owns]))]:pr-0 has-[>kbd]:pr-2\",\n        \"block-start\":\n          \"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3\",\n        \"block-end\":\n          \"order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3\",\n      },\n    },\n    defaultVariants: {\n      align: \"inline-start\",\n    },\n  },\n);\n\ntype InputGroupAddonAlign = NonNullable<\n  VariantProps<typeof inputGroupAddonVariants>[\"align\"]\n>;\n\nconst InputGroupAddonContext =\n  React.createContext<InputGroupAddonAlign>(\"inline-start\");\n\nexport type InputGroupAddonProps = React.ComponentProps<\"div\"> &\n  VariantProps<typeof inputGroupAddonVariants>;\n\nfunction InputGroupAddon({\n  className,\n  align,\n  onClick,\n  ...props\n}: InputGroupAddonProps) {\n  const resolvedAlign = align ?? \"inline-start\";\n\n  return (\n    <InputGroupAddonContext.Provider value={resolvedAlign}>\n      <div\n        role=\"group\"\n        data-slot=\"input-group-addon\"\n        data-align={resolvedAlign}\n        className={cn(\n          inputGroupAddonVariants({ align: resolvedAlign }),\n          className,\n        )}\n        onClick={(event) => {\n          onClick?.(event);\n          // Leave focus alone for the addon's own controls. Scoped to the\n          // addon, since a dialog around the group has a tabindex too.\n          const control = (event.target as HTMLElement).closest(\n            \"button, a, input, select, textarea, [role=button], [tabindex]\",\n          );\n          // Portaled popups bubble here through React but sit outside the DOM.\n          if (\n            event.defaultPrevented ||\n            !event.currentTarget.contains(event.target as Node) ||\n            (control && event.currentTarget.contains(control))\n          )\n            return;\n          event.currentTarget.parentElement\n            ?.querySelector<HTMLElement>(\":scope > input, :scope > textarea\")\n            ?.focus();\n        }}\n        {...props}\n      />\n    </InputGroupAddonContext.Provider>\n  );\n}\n\nconst inputGroupButtonVariants = cva(\n  [\n    \"border-2 border-transparent shadow-none\",\n    // Hover and press on a quiet button: the label color at 10% / 14%\n    \"data-[variant=ghost]:[--btn-bg-hover:color-mix(in_oklab,currentColor_10%,transparent)] data-[variant=ghost]:[--btn-bg-active:color-mix(in_oklab,currentColor_14%,transparent)]\",\n  ],\n  {\n    variants: {\n      size: {\n        default: \"px-2.5\",\n        icon: \"\",\n      },\n      // Inline: stretched to the shell's height, with box corners the\n      // paint's + 2px so paint, gap, and focus ring (at offset 0) are\n      // concentric. Block: a fixed height in a toolbar row.\n      placement: {\n        inline:\n          \"self-stretch rounded-[calc(var(--input-group-item-radius)+2px)] before:rounded-(--input-group-item-radius) focus-visible:outline-offset-0\",\n        block: \"\",\n      },\n    },\n    compoundVariants: [\n      {\n        size: \"default\",\n        placement: \"inline\",\n        className: \"h-auto sm:not-pointer-coarse:h-auto\",\n      },\n      {\n        size: \"icon\",\n        placement: \"inline\",\n        className: \"size-auto aspect-square sm:not-pointer-coarse:size-auto\",\n      },\n      {\n        size: \"default\",\n        placement: \"block\",\n        className: \"h-8 sm:not-pointer-coarse:h-8\",\n      },\n      {\n        size: \"icon\",\n        placement: \"block\",\n        className: \"size-8 sm:not-pointer-coarse:size-8\",\n      },\n    ],\n    defaultVariants: {\n      size: \"default\",\n      placement: \"inline\",\n    },\n  },\n);\n\nexport type InputGroupButtonProps = Omit<\n  React.ComponentProps<typeof Button>,\n  \"size\"\n> & {\n  /** `icon` keeps an icon-only button square. */\n  size?: \"default\" | \"icon\";\n};\n\nfunction InputGroupButton({\n  className,\n  variant = \"ghost\",\n  size = \"default\",\n  ...props\n}: InputGroupButtonProps) {\n  const align = React.use(InputGroupAddonContext);\n  const placement = align.startsWith(\"inline\") ? \"inline\" : \"block\";\n\n  return (\n    <Button\n      data-slot=\"input-group-button\"\n      variant={variant}\n      size={size === \"icon\" ? \"icon_sm\" : \"sm\"}\n      className={cn(inputGroupButtonVariants({ size, placement }), className)}\n      {...props}\n    />\n  );\n}\n\nfunction InputGroupText({ className, ...props }: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"input-group-text\"\n      className={cn(\n        \"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport type InputGroupInputProps = InputProps;\n\nfunction InputGroupInput({ className, size, ...props }: InputGroupInputProps) {\n  return (\n    <Input\n      data-slot=\"input-group-control\"\n      size={size}\n      className={cn(\n        \"h-auto flex-1 self-stretch rounded-none border-0 bg-transparent shadow-none focus-visible:outline-0 aria-invalid:outline-0 sm:not-pointer-coarse:h-auto dark:bg-transparent\",\n        // Small padding in a small group, weak enough that addon-side\n        // padding still wins\n        \"in-[[data-slot=input-group][data-size=sm]]:px-2.5 in-[[data-slot=input-group][data-size=sm]]:py-1.5\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction InputGroupTextarea({\n  className,\n  ...props\n}: React.ComponentProps<typeof Textarea>) {\n  return (\n    <Textarea\n      data-slot=\"input-group-control\"\n      className={cn(\n        \"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:outline-0 aria-invalid:outline-0 dark:bg-transparent\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nexport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupText,\n  InputGroupInput,\n  InputGroupTextarea,\n};\n",
      "type": "registry:ui",
      "target": "components/ui/cubby-ui/input-group.tsx"
    }
  ],
  "type": "registry:ui"
}