{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input-group",
  "title": "input-group",
  "description": "A field with addons: icons, text, keys or buttons round one input or text area, at inline-start, inline-end, block-start or block-end. size default is the Input's 40px pill; lg is a 56px field with 18px text and rounded-3xl corners, for a search composer.",
  "dependencies": [
    "class-variance-authority@^0.7"
  ],
  "registryDependencies": [
    "@dwellir/dwellir-theme",
    "@dwellir/utils",
    "@dwellir/button"
  ],
  "files": [
    {
      "path": "components/ui/input-group.tsx",
      "content": "'use client';\n\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { type ComponentProps, createContext, type MouseEvent, useContext } from 'react';\nimport { Button } from '@/components/ui/button';\nimport { cn } from '@/lib/utils';\n\ntype InputGroupSize = 'default' | 'lg';\n\n/* The group's size reaches its addons and controls, so callers set it once. */\nconst InputGroupSizeContext = createContext<InputGroupSize>('default');\n\nconst CONTROL = '[data-slot=input-group-control]';\n\n/*\n * A field with addons: icons, text, keys or buttons round one InputGroupInput or\n * InputGroupTextarea.\n * The group draws the field, with the Input's solid fill, calm edge, hover\n * edge, focus ring and red edge under aria-invalid; the control inside is bare.\n * The ring follows the control's focus, not an addon button's, which has its own.\n */\nconst inputGroupVariants = cva(\n  [\n    'input-group group/input-group relative flex w-full min-w-0 items-center border border-input text-foreground transition-[border-color] duration-200 outline-none hover:border-input-hover',\n    'has-[>[data-align^=block]]:flex-col has-[>[data-align^=block]]:items-stretch',\n    'has-[>[data-align=inline-start]]:[&>input]:pl-2 has-[>[data-align=inline-end]]:[&>input]:pr-2',\n    'has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:outline-solid 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-ring',\n    'has-[[data-slot=input-group-control][aria-invalid=true]]:border-destructive has-[[data-slot=input-group-control][aria-invalid=true]]:outline-destructive aria-invalid:border-destructive aria-invalid:outline-destructive',\n    'has-[[data-slot=input-group-control]:disabled]:pointer-events-none has-[[data-slot=input-group-control]:disabled]:opacity-50 data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50',\n  ],\n  {\n    variants: {\n      /*\n       * The size reaches the addons and controls through context and\n       * data-size, and input-group corners the group from it (rows or a\n       * Textarea cap at 2xl, the large composer at 3xl).\n       */\n      size: {\n        default: '',\n        /*\n         * A composer. Its focus ring lies on the edge, not 2px outside it: on\n         * a field this large a ring beside the edge reads as a double frame.\n         */\n        lg: 'has-[[data-slot=input-group-control]:focus-visible]:-outline-offset-1',\n      },\n      surface: {\n        /* The default: the field's solid fill. */\n        field: 'bg-field',\n        /*\n         * Frosted glass for a composer over the aurora, such as the dashboard's\n         * home search: a sheer 40% pane (input-group), the blur, and the card\n         * shadow. Only over the aurora: the blur redraws every frame. Focus\n         * draws a 2px ring on the edge in foreground/50 instead of the accent\n         * ring, which reads stark on a pane this large: 3:1 against the fill\n         * in light and dark, and twice the hover edge's width.\n         */\n        glass: [\n          'border-foreground/12 glass shadow-card',\n          'has-[[data-slot=input-group-control]:focus-visible]:border-transparent has-[[data-slot=input-group-control]:focus-visible]:outline-foreground/50 has-[[data-slot=input-group-control]:focus-visible]:-outline-offset-1',\n        ],\n      },\n    },\n    defaultVariants: { size: 'default', surface: 'field' },\n  },\n);\n\nfunction InputGroup({\n  className,\n  size,\n  surface,\n  ...props\n}: ComponentProps<'div'> & VariantProps<typeof inputGroupVariants>) {\n  const groupSize = size ?? 'default';\n  return (\n    <InputGroupSizeContext.Provider value={groupSize}>\n      {/* biome-ignore lint/a11y/useSemanticElements: one field and its addons, not a form section, so no fieldset. */}\n      <div\n        data-slot=\"input-group\"\n        data-size={groupSize}\n        data-surface={surface ?? 'field'}\n        role=\"group\"\n        className={cn(inputGroupVariants({ size: groupSize, surface }), className)}\n        {...props}\n      />\n    </InputGroupSizeContext.Provider>\n  );\n}\n\n/*\n * inline-start and inline-end sit beside the control; block-start and\n * block-end take a row above or below it, such as a composer's filters.\n */\nconst inputGroupAddonVariants = cva(\n  \"flex h-auto cursor-text items-center gap-2 text-muted-foreground select-none [&>svg:not([class*='size-'])]:size-4\",\n  {\n    variants: {\n      align: {\n        'inline-start': 'order-first justify-center',\n        'inline-end': 'order-last justify-center',\n        /* A row wraps on a narrow screen rather than widen the field. */\n        'block-start': 'order-first w-full flex-wrap justify-start',\n        'block-end': 'order-last w-full flex-wrap justify-start',\n      },\n      size: {\n        default: 'text-sm',\n        lg: 'text-base',\n      },\n    },\n    compoundVariants: [\n      { align: 'inline-start', size: 'default', className: 'pl-4 has-[>button]:pl-1' },\n      { align: 'inline-end', size: 'default', className: 'pr-4 has-[>button]:pr-1' },\n      { align: 'inline-start', size: 'lg', className: 'pl-5 has-[>button]:pl-2' },\n      { align: 'inline-end', size: 'lg', className: 'pr-5 has-[>button]:pr-2' },\n      { align: 'block-start', size: 'default', className: 'px-4 pt-3' },\n      { align: 'block-end', size: 'default', className: 'px-4 pb-3' },\n      { align: 'block-start', size: 'lg', className: 'px-4 pt-4' },\n      { align: 'block-end', size: 'lg', className: 'px-4 pb-4' },\n    ],\n    defaultVariants: { align: 'inline-start', size: 'default' },\n  },\n);\n\n/* A click on the addon's free space focuses the control, as a click on a label does. */\nfunction focusControl(event: MouseEvent<HTMLDivElement>) {\n  if ((event.target as HTMLElement).closest('button, a, input, textarea, select')) return;\n  event.currentTarget.parentElement?.querySelector<HTMLElement>(CONTROL)?.focus();\n}\n\nfunction InputGroupAddon({\n  className,\n  align,\n  onClick,\n  ...props\n}: ComponentProps<'div'> & Omit<VariantProps<typeof inputGroupAddonVariants>, 'size'>) {\n  const size = useContext(InputGroupSizeContext);\n  const placement = align ?? 'inline-start';\n  return (\n    // biome-ignore lint/a11y/useSemanticElements: a field's addons, not a form section, so no fieldset.\n    <div\n      role=\"group\"\n      data-slot=\"input-group-addon\"\n      data-align={placement}\n      className={cn(inputGroupAddonVariants({ align: placement, size }), className)}\n      onClick={(event) => {\n        onClick?.(event);\n        if (!event.defaultPrevented) focusControl(event);\n      }}\n      {...props}\n    />\n  );\n}\n\ntype ButtonProps = ComponentProps<typeof Button>;\n\n/* Button's ghost look by default, in the sizes that fit inside the field. */\nfunction InputGroupButton({\n  type = 'button',\n  variant = 'ghost',\n  size,\n  ...props\n}: Omit<ButtonProps, 'size' | 'variant'> & {\n  /* default is upstream's name for primary. */\n  variant?: ButtonProps['variant'] | 'default';\n  /** Default: xs in a default group, sm in a size=\"lg\" group. */\n  size?: 'xs' | 'sm' | 'default' | 'icon-xs' | 'icon-sm' | 'icon';\n}) {\n  const groupSize = useContext(InputGroupSizeContext);\n  return (\n    <Button\n      type={type}\n      variant={variant === 'default' ? 'primary' : variant}\n      size={size ?? (groupSize === 'lg' ? 'sm' : 'xs')}\n      {...props}\n    />\n  );\n}\n\nconst inputGroupTextVariants = cva(\n  \"flex items-center gap-2 text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n  {\n    variants: { size: { default: 'text-sm', lg: 'text-base' } },\n    defaultVariants: { size: 'default' },\n  },\n);\n\nfunction InputGroupText({ className, ...props }: ComponentProps<'span'>) {\n  const size = useContext(InputGroupSizeContext);\n  return (\n    <span\n      data-slot=\"input-group-text\"\n      className={cn(inputGroupTextVariants({ size }), className)}\n      {...props}\n    />\n  );\n}\n\n/* The bare control: the group draws its edge, fill and states. iOS zooms in under 16px text, so touch screens get text-lg at every width. */\nconst CONTROL_BASE =\n  'min-w-0 grow bg-transparent text-foreground outline-none selection:bg-accent placeholder:text-muted-foreground disabled:cursor-not-allowed';\n\nconst inputGroupInputVariants = cva(CONTROL_BASE, {\n  variants: {\n    size: {\n      default: 'h-9.5 pointer-coarse:h-10.5 px-4 text-lg md:pointer-fine:text-sm',\n      lg: 'h-13.5 px-5 text-lg',\n    },\n  },\n  defaultVariants: { size: 'default' },\n});\n\nfunction InputGroupInput({ className, ...props }: ComponentProps<'input'>) {\n  const size = useContext(InputGroupSizeContext);\n  return (\n    <input\n      data-slot=\"input-group-control\"\n      className={cn(inputGroupInputVariants({ size }), className)}\n      {...props}\n    />\n  );\n}\n\nconst inputGroupTextareaVariants = cva(`${CONTROL_BASE} field-sizing-content resize-none`, {\n  variants: {\n    size: {\n      default: 'min-h-16 px-4 py-2.5 text-lg md:pointer-fine:text-sm',\n      lg: 'min-h-24 px-5 py-4 text-lg',\n    },\n  },\n  defaultVariants: { size: 'default' },\n});\n\nfunction InputGroupTextarea({ className, ...props }: ComponentProps<'textarea'>) {\n  const size = useContext(InputGroupSizeContext);\n  return (\n    <textarea\n      data-slot=\"input-group-control\"\n      className={cn(inputGroupTextareaVariants({ size }), className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupInput,\n  InputGroupText,\n  InputGroupTextarea,\n  inputGroupAddonVariants,\n  inputGroupVariants,\n};\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}