{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input-group",
  "title": "Input Group",
  "description": "One field with text, icons, buttons or a select attached to it, sharing a single border and focus ring, in four layouts.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/miseui/ui/input-group.tsx",
      "content": "import type * as React from \"react\"\nimport { createContext, useContext, useId, useMemo } from \"react\"\nimport { ChevronDown } from \"lucide-react\"\nimport {\n  ADDON_CLASS,\n  BUTTON_BASE_CLASS,\n  BUTTON_SHAPE_CLASS,\n  BUTTON_TONE_CLASS,\n  CHEVRON_CLASS,\n  FIELD_STATE_CLASS,\n  INPUT_BASE_CLASS,\n  INPUT_CLASS,\n  ROOT_CLASS,\n  SELECT_CLASS,\n  SELECT_WRAP_CLASS,\n  shouldFocusField,\n  type InputGroupVariant,\n} from \"@/registry/miseui/ui/input-group-classes\"\nimport { cn } from \"@/registry/miseui/lib/utils\"\n\ninterface InputGroupContextValue {\n  variant: InputGroupVariant\n  inputId: string\n  /** The name for the input when the label is not drawn. */\n  fallbackName: string | undefined\n  disabled: boolean\n}\n\nconst InputGroupContext = createContext<InputGroupContextValue>({\n  variant: \"attached\",\n  inputId: \"\",\n  fallbackName: undefined,\n  disabled: false,\n})\n\nconst DRAWS_LABEL: Record<InputGroupVariant, boolean> = { attached: false, inset: false, stacked: true, inline: true }\n\ntype InputGroupProps = React.ComponentProps<\"div\"> & {\n  /** `attached` addons as tinted cells with hairlines, `inset` addons inside the field, `stacked` label over the value in one box, `inline` label and value on one filled row. */\n  variant?: InputGroupVariant\n  /** Drawn by `stacked` and `inline`. The other two leave it to a surrounding Field and use it as the input's name. */\n  label?: string\n  /** Disables the input and every control in the group. */\n  disabled?: boolean\n}\n\nfunction InputGroup({ variant = \"attached\", label, disabled = false, className, children, onMouseDown, ...props }: InputGroupProps) {\n  const inputId = useId()\n  const drawsLabel = DRAWS_LABEL[variant] && Boolean(label)\n  const context = useMemo(\n    () => ({ variant, inputId, fallbackName: drawsLabel ? undefined : label, disabled }),\n    [variant, inputId, drawsLabel, label, disabled],\n  )\n  const visibleLabel = drawsLabel ? (\n    <label htmlFor={inputId} data-slot=\"input-group-label\" className={cn(\"text-[13px] leading-5\", variant === \"inline\" ? \"shrink-0 text-foreground\" : \"text-muted-foreground\")}>\n      {label}\n    </label>\n  ) : null\n  return (\n    <InputGroupContext.Provider value={context}>\n      <div\n        role=\"group\"\n        aria-label={label}\n        data-slot=\"input-group\"\n        data-variant={variant}\n        className={cn(\"w-full min-w-0\", ROOT_CLASS[variant], FIELD_STATE_CLASS, className)}\n        // Mouse, not pointer: a touch that starts a scroll must not pop the keyboard open. preventDefault keeps an already focused input from blurring.\n        onMouseDown={(event) => {\n          onMouseDown?.(event)\n          if (event.defaultPrevented || !shouldFocusField(event.target as Element)) return\n          event.preventDefault()\n          event.currentTarget.querySelector<HTMLInputElement>(\"[data-slot='input-group-input']\")?.focus()\n        }}\n        {...props}\n      >\n        {visibleLabel}\n        {variant === \"stacked\" || variant === \"inline\" ? (\n          <div data-slot=\"input-group-row\" className=\"flex min-w-0 flex-1 items-center gap-2\">\n            {children}\n          </div>\n        ) : (\n          children\n        )}\n      </div>\n    </InputGroupContext.Provider>\n  )\n}\n\nfunction InputGroupAddon({ className, ...props }: React.ComponentProps<\"span\">) {\n  const { variant } = useContext(InputGroupContext)\n  return (\n    <span\n      data-slot=\"input-group-addon\"\n      className={cn(\"text-sm text-muted-foreground select-none [&_svg]:size-4 [&_svg]:shrink-0\", ADDON_CLASS[variant], className)}\n      {...props}\n    />\n  )\n}\n\nfunction InputGroupInput({ className, id, disabled, \"aria-label\": ariaLabel, ...props }: React.ComponentProps<\"input\">) {\n  const { variant, inputId, fallbackName, disabled: groupDisabled } = useContext(InputGroupContext)\n  return (\n    <input\n      id={id ?? inputId}\n      aria-label={ariaLabel ?? fallbackName}\n      disabled={disabled || groupDisabled}\n      data-slot=\"input-group-input\"\n      className={cn(INPUT_BASE_CLASS, INPUT_CLASS[variant], className)}\n      {...props}\n    />\n  )\n}\n\ntype InputGroupButtonProps = React.ComponentProps<\"button\"> & {\n  /** The main action of the field, in the accent. */\n  primary?: boolean\n}\n\nfunction InputGroupButton({ className, primary = false, disabled, type = \"button\", ...props }: InputGroupButtonProps) {\n  const { variant, disabled: groupDisabled } = useContext(InputGroupContext)\n  return (\n    <button\n      type={type}\n      data-slot=\"input-group-button\"\n      data-primary={primary ? \"true\" : undefined}\n      disabled={disabled || groupDisabled}\n      className={cn(BUTTON_BASE_CLASS, BUTTON_SHAPE_CLASS[variant], BUTTON_TONE_CLASS[primary ? \"primary\" : \"quiet\"], className)}\n      {...props}\n    />\n  )\n}\n\n/** The name is required: nothing else in the field says what this choice is. */\ntype InputGroupSelectProps = Omit<React.ComponentProps<\"select\">, \"aria-label\"> & { \"aria-label\": string }\n\n/** A native select on purpose: on a phone it opens the system picker, which is what people expect inside a field. */\nfunction InputGroupSelect({ className, disabled, children, ...props }: InputGroupSelectProps) {\n  const { variant, disabled: groupDisabled } = useContext(InputGroupContext)\n  return (\n    <span data-slot=\"input-group-select\" className={cn(\"relative flex shrink-0 items-center\", SELECT_WRAP_CLASS[variant])}>\n      <select\n        disabled={disabled || groupDisabled}\n        className={cn(\n          \"h-full cursor-pointer appearance-none bg-transparent text-base text-foreground outline-none md:text-sm disabled:cursor-not-allowed\",\n          SELECT_CLASS[variant],\n          className,\n        )}\n        {...props}\n      >\n        {children}\n      </select>\n      <ChevronDown aria-hidden=\"true\" className={cn(\"pointer-events-none absolute size-3.5 text-muted-foreground\", CHEVRON_CLASS[variant])} />\n    </span>\n  )\n}\n\nexport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n  InputGroupButton,\n  InputGroupSelect,\n  type InputGroupProps,\n  type InputGroupButtonProps,\n  type InputGroupSelectProps,\n  type InputGroupVariant,\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/input-group-classes.ts",
      "content": "import { cn } from \"@/registry/miseui/lib/utils\"\n\ntype InputGroupVariant = \"attached\" | \"inset\" | \"stacked\" | \"inline\"\n\n/** Focus and error live on the root, so input, addons and buttons read as one field with one ring. */\nconst FIELD_STATE_CLASS = cn(\n  \"transition-[border-color,box-shadow] duration-(--duration-fast) ease-(--ease-mise) cursor-text\",\n  \"has-[input:focus-visible,select:focus-visible]:border-ring has-[input:focus-visible,select:focus-visible]:ring-[3px] has-[input:focus-visible,select:focus-visible]:ring-ring/50\",\n  \"has-[[aria-invalid=true]]:border-destructive has-[[aria-invalid=true]]:ring-destructive/20 dark:has-[[aria-invalid=true]]:ring-destructive/40\",\n  \"has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50\",\n)\n\nconst ROOT_CLASS: Record<InputGroupVariant, string> = {\n  attached: \"flex h-9 items-stretch divide-x divide-input rounded-md border border-input shadow-xs pointer-coarse:h-10\",\n  inset: \"flex h-9 items-center gap-2 rounded-md border border-input px-3 shadow-xs pointer-coarse:h-10\",\n  stacked: \"grid gap-1 rounded-md border border-input px-3 py-2 shadow-xs\",\n  inline: \"flex min-h-10 items-center gap-3 rounded-md border border-transparent bg-foreground/[0.05] px-3\",\n}\n\nconst ADDON_CLASS: Record<InputGroupVariant, string> = {\n  attached: \"flex shrink-0 items-center gap-2 bg-foreground/[0.04] px-3 first:rounded-l-[inherit] last:rounded-r-[inherit]\",\n  inset: \"flex shrink-0 items-center gap-2\",\n  stacked: \"flex shrink-0 items-center gap-2\",\n  inline: \"flex shrink-0 items-center gap-2\",\n}\n\n/** min-w-24: below that the placeholder is cut instead of moving, and a typed value has no room to be read. */\nconst INPUT_BASE_CLASS = cn(\n  \"w-0 min-w-24 flex-1 bg-transparent text-base text-foreground outline-none md:text-sm\",\n  \"placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground disabled:cursor-not-allowed\",\n)\n\nconst INPUT_CLASS: Record<InputGroupVariant, string> = {\n  attached: \"px-3 first:rounded-l-[inherit] last:rounded-r-[inherit]\",\n  inset: \"h-full\",\n  stacked: \"h-6\",\n  inline: \"text-right\",\n}\n\nconst BUTTON_BASE_CLASS = cn(\n  \"relative inline-flex shrink-0 items-center justify-center gap-2 font-medium whitespace-nowrap outline-none select-none\",\n  \"transition-[color,background-color] duration-(--duration-fast) ease-(--ease-mise) active:duration-0\",\n  \"focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:ring-inset\",\n  \"disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n)\n\nconst HIT_AREA_CLASS =\n  \"pointer-coarse:after:absolute pointer-coarse:after:top-1/2 pointer-coarse:after:left-1/2 pointer-coarse:after:size-10 pointer-coarse:after:-translate-x-1/2 pointer-coarse:after:-translate-y-1/2\"\n\nconst BUTTON_SHAPE_CLASS: Record<InputGroupVariant, string> = {\n  attached: \"h-full px-3 text-sm first:rounded-l-[inherit] last:rounded-r-[inherit]\",\n  inset: cn(\"-mr-2 h-7 rounded-sm px-2 text-[13px]\", HIT_AREA_CLASS),\n  stacked: cn(\"h-6 rounded-sm px-2 text-[13px]\", HIT_AREA_CLASS),\n  inline: cn(\"h-7 rounded-sm px-2 text-[13px]\", HIT_AREA_CLASS),\n}\n\nconst BUTTON_TONE_CLASS = {\n  quiet: \"text-foreground hover:bg-foreground/[0.07] active:bg-foreground/10\",\n  primary: \"bg-mise text-mise-foreground hover:bg-mise/90 active:bg-mise/80\",\n}\n\nconst SELECT_WRAP_CLASS: Record<InputGroupVariant, string> = {\n  attached: \"bg-foreground/[0.04] first:rounded-l-[inherit] last:rounded-r-[inherit]\",\n  inset: \"\",\n  stacked: \"\",\n  inline: \"\",\n}\n\n/** On touch `stacked` grows to 40 px and takes the extra back with a negative margin, so the box keeps its height. */\nconst SELECT_CLASS: Record<InputGroupVariant, string> = {\n  attached: \"pr-8 pl-3\",\n  inset: \"pr-5\",\n  stacked: \"h-6 pr-5 pointer-coarse:-my-2 pointer-coarse:h-10\",\n  inline: \"h-7 pr-5 pointer-coarse:h-10\",\n}\n\nconst CHEVRON_CLASS: Record<InputGroupVariant, string> = {\n  attached: \"right-2\",\n  inset: \"right-0\",\n  stacked: \"right-0\",\n  inline: \"right-0\",\n}\n\n/** Text inside the field that does not start a click on a real control sends focus to the input, as a whole field should. */\nfunction shouldFocusField(target: Element) {\n  return !target.closest(\"input,select,button,a,label\")\n}\n\nexport {\n  ADDON_CLASS,\n  BUTTON_BASE_CLASS,\n  BUTTON_SHAPE_CLASS,\n  BUTTON_TONE_CLASS,\n  CHEVRON_CLASS,\n  FIELD_STATE_CLASS,\n  INPUT_BASE_CLASS,\n  INPUT_CLASS,\n  ROOT_CLASS,\n  SELECT_CLASS,\n  SELECT_WRAP_CLASS,\n  shouldFocusField,\n  type InputGroupVariant,\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "forms"
  ],
  "type": "registry:ui"
}