# InputGroup

> An input with leading and trailing addons (icon, text affix or button) inside one bordered control that matches Input height.

Source: https://docs.nasaqui.com/components/input-group

## Install

```bash
npx shadcn@latest add https://docs.nasaqui.com/r/input-group.json
```

A single bordered control that holds an input plus addons on its start and end edges: a search icon, an
`https://` prefix, a currency unit, a show-password button. The group owns the border, focus ring and invalid
state, so the parts read as one field and the height matches `Input` at every density.

## When to use

- A search box with an icon, a URL or currency field with a unit, a password field with a toggle.

## When not to use

- Plain text entry with no addon: use [`Input`](https://docs.nasaqui.com/components/field).
- Picking from a list: use [`Select`](https://docs.nasaqui.com/components/select) or [`Combobox`](https://docs.nasaqui.com/components/combobox).

## Import

```tsx
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { Field, FieldLabel, InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@fadymondy/nasaq/web";

export function WebsiteField() {
  return (
    <Field>
      <FieldLabel>Website</FieldLabel>
      <InputGroup>
        <InputGroupAddon>
          <InputGroupText dir="ltr">https://</InputGroupText>
        </InputGroupAddon>
        <InputGroupInput ltr placeholder="nasaq.app" />
      </InputGroup>
    </Field>
  );
}
```

## Anatomy

```
InputGroup            data-slot="input-group" (role="group")
├─ InputGroupAddon    data-slot="input-group-addon", align="start" | "end"
│  └─ InputGroupText  data-slot="input-group-text"  (or an icon or a Button)
└─ InputGroupInput    data-slot="input-group-input"  (Base UI Input)
```

## API

**InputGroup**: a `div`. Takes all `div` props.

**InputGroupAddon**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `align` | `"start" \| "end"` | `"start"` | Edge the addon sits on. Logical, so `end` is the left edge in RTL. |

**InputGroupText**: a `span` for affixes such as `SAR` or `https://`.

**InputGroupInput**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `ltr` | `boolean` | `false` | Force left-to-right entry and `text-start`, for emails, URLs and codes in Arabic forms. |

All other props go to the Base UI `Input`.

## Examples

Password toggle with a button addon:

```tsx
import { Button, InputGroup, InputGroupAddon, InputGroupInput } from "@fadymondy/nasaq/web";
import { Eye } from "lucide-react";

export function Password() {
  return (
    <InputGroup>
      <InputGroupInput type="password" />
      <InputGroupAddon align="end" className="pe-1.5">
        <Button variant="ghost" size="icon-sm" aria-label="Show password">
          <Eye aria-hidden="true" />
        </Button>
      </InputGroupAddon>
    </InputGroup>
  );
}
```

Currency in Arabic:

```tsx
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@fadymondy/nasaq/web";

export function Price() {
  return (
    <InputGroup>
      <InputGroupInput inputMode="decimal" placeholder="0.00" />
      <InputGroupAddon align="end">
        <InputGroupText>ر.س</InputGroupText>
      </InputGroupAddon>
    </InputGroup>
  );
}
```

## Accessibility

| Key | Action |
| --- | --- |
| Tab | Moves to the input, then to any button addon. |

The group has `role="group"`. Wrap in `Field` with `FieldLabel` so the input is labelled; icon-only button
addons need an `aria-label` you localise. Decorative icons take `aria-hidden`.

## RTL & i18n

Addons use logical padding, so `align="start"` sits on the right in RTL. Use `ltr` on the input and `dir="ltr"`
on `InputGroupText` for URLs and other Latin content.

## Styling & tokens

Uses `border-input`, `bg-card`, `border-nq-focus`, `border-nq-danger`, `h-control`, `rounded-control`. State
attributes: `data-invalid` on the input turns the group border danger; a disabled input dims the group.
Extend with `className`.

## Do / Don't

- Do keep addons short. Do give icon buttons a label.
- Don't put a second bordered control inside; use `variant="ghost"` buttons.

## Related

- [Field](https://docs.nasaqui.com/components/field)
- [Select](https://docs.nasaqui.com/components/select)
- [Combobox](https://docs.nasaqui.com/components/combobox)

## Lab

`https://docs.nasaqui.com/?path=/docs/components-forms-inputgroup--docs`

## Code

### React

```tsx
import { Field, FieldLabel, InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@fadymondy/nasaq/web";

export function WebsiteField() {
  return (
    <Field>
      <FieldLabel>Website</FieldLabel>
      <InputGroup>
        <InputGroupAddon>
          <InputGroupText dir="ltr">https://</InputGroupText>
        </InputGroupAddon>
        <InputGroupInput ltr placeholder="nasaq.app" />
      </InputGroup>
    </Field>
  );
}
```

### shadcn

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@/components/ui/input-group";

export function WebsiteField() {
  return (
    <Field>
      <FieldLabel>Website</FieldLabel>
      <InputGroup>
        <InputGroupAddon>
          <InputGroupText dir="ltr">https://</InputGroupText>
        </InputGroupAddon>
        <InputGroupInput ltr placeholder="nasaq.app" />
      </InputGroup>
    </Field>
  );
}
```

### Vue

```vue
<script setup lang="ts">
import { NqInputGroup, NqInputGroupAddon, NqInputGroupInput, NqInputGroupText } from "@fadymondy/nasaq/vue";
</script>

<template>
  <div class="flex flex-col gap-1.5">
    <label for="website" class="text-label text-foreground">Website</label>
    <NqInputGroup>
      <NqInputGroupAddon>
        <NqInputGroupText dir="ltr">https://</NqInputGroupText>
      </NqInputGroupAddon>
      <NqInputGroupInput id="website" ltr placeholder="nasaq.app" />
    </NqInputGroup>
  </div>
</template>
```

### Blade

```blade
<div class="flex flex-col gap-1.5">
    <label for="website" class="text-label text-foreground">Website</label>
    <x-nq::input-group>
        <x-nq::input-group.addon>
            <x-nq::input-group.text dir="ltr">https://</x-nq::input-group.text>
        </x-nq::input-group.addon>
        <x-nq::input-group.input id="website" ltr placeholder="nasaq.app" />
    </x-nq::input-group>
</div>
```

### HTML + Alpine

```html
<div class="flex flex-col gap-1.5">
    <label for="website" class="text-label text-foreground">Website</label>
    <div role="group" data-slot="input-group"
    class="group/input-group flex h-control min-h-[var(--nq-touch-min,0px)] w-full min-w-0 items-center overflow-hidden rounded-control border border-input bg-card text-body text-foreground transition-colors duration-150 ease-nq focus-within:border-nq-focus focus-within:outline-1 focus-within:outline-nq-focus has-[[data-invalid]]:border-nq-danger has-[[aria-invalid=true]]:border-nq-danger has-[input:disabled]:cursor-not-allowed has-[input:disabled]:opacity-50"><div data-slot="input-group-addon" data-align="start"
    class="flex h-full shrink-0 items-center gap-1.5 text-body-sm text-muted-foreground [&_svg]:size-4 order-first ps-3 pe-1"><span data-slot="input-group-text" dir="ltr" class="select-none whitespace-nowrap">https://</span></div>
        <input data-slot="input-group-input"  dir="ltr"     id="website" placeholder="nasaq.app" class="h-full min-w-0 flex-1 border-0 bg-transparent px-3 text-body text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed pointer-coarse:text-[16px] text-start" /></div>
</div>
```
