# Select

> Pick one value from a short list. A Field-style trigger that opens a DropdownMenu-style list. Wraps Base UI Select.

Source: https://docs.nasaqui.com/components/select

## Install

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

A form control for choosing one value from a list. The trigger looks like an `Input`; the list looks like a
`DropdownMenu`. Built on Base UI `Select`, so it has typeahead, keyboard navigation and a hidden native input
for forms (`name`).

## When to use

- One choice from 4 to ~15 known options in a form (type, priority, product).

## When not to use

- Two or three options that should stay visible: use [`ChipGroup`](https://docs.nasaqui.com/components/chip-group) or radios.
- Actions rather than a value: use [`DropdownMenu`](https://docs.nasaqui.com/components/dropdown-menu).
- Long, searchable lists: use the command palette pattern.

## Import

```tsx
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"
```

## Quick start

```tsx
import { Field, FieldLabel, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@fadymondy/nasaq/web";

const TYPES = [
  { value: "bug", label: "Bug" },
  { value: "feature", label: "Feature request" },
  { value: "question", label: "Question" },
];

export function TypeField() {
  return (
    <Field>
      <FieldLabel>Type</FieldLabel>
      <Select items={TYPES} defaultValue="bug" name="type">
        <SelectTrigger>
          <SelectValue />
        </SelectTrigger>
        <SelectContent>
          {TYPES.map((t) => (
            <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>
          ))}
        </SelectContent>
      </Select>
    </Field>
  );
}
```

## Anatomy

```
Select                     Base UI Select.Root (pass items so the trigger can show labels)
├─ SelectTrigger           data-slot="select-trigger", chevron icon appended
│  └─ SelectValue          data-slot="select-value"
└─ SelectContent           Portal + positioner + popup + list   data-slot="select-content"
   ├─ SelectGroup          Base UI Select.Group
   │  └─ SelectLabel       data-slot="select-label" (inside a group only)
   ├─ SelectItem           data-slot="select-item", check at the inline-start
   └─ SelectSeparator      data-slot="select-separator"
```

## API

`Select` = `Select.Root` (`value?`, `defaultValue?`, `onValueChange?`, `items?`, `name?`, `disabled?`,
`required?`, `multiple?`). `SelectGroup` = `Select.Group`. `items` is optional: when it is omitted, the trigger label is
derived from the inline `<SelectItem>` children (value and text), so the selected label shows without a list.

### `SelectContent`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `side?` | `Select.Positioner` `side` | `"bottom"` | Side of the trigger to open on. |
| `align?` | `"start" \| "center" \| "end"` | `"start"` | Alignment against the trigger. |
| `sideOffset?` | `number` | `4` | Gap to the trigger in px. |
| `alignItemWithTrigger?` | `boolean` | `false` | Overlay the selected item on the trigger (native feel). |

The popup is at least as wide as the trigger and scrolls within the available height.

### `SelectValue`

Base UI `Select.Value`. Pass `placeholder` for the empty state (muted).

### `SelectItem`

Base UI `Select.Item` props: `value` (required), `disabled?`, `label?`. Children are the item text.

## Accessibility

- The trigger is a combobox button; the list is a listbox with roving focus and typeahead.
- Wrap it in a `Field` with a `FieldLabel` so the trigger is labelled.
- RTL: the check and chevron sit on logical sides and mirror with the page.

## Code

### React

```tsx
import { Field, FieldLabel, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@fadymondy/nasaq/web";

const TYPES = [
  { value: "bug", label: "Bug" },
  { value: "feature", label: "Feature request" },
  { value: "question", label: "Question" },
];

export function TypeField() {
  return (
    <Field>
      <FieldLabel>Type</FieldLabel>
      <Select items={TYPES} defaultValue="bug" name="type">
        <SelectTrigger>
          <SelectValue />
        </SelectTrigger>
        <SelectContent>
          {TYPES.map((t) => (
            <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>
          ))}
        </SelectContent>
      </Select>
    </Field>
  );
}
```

### shadcn

```tsx
import { Field, FieldLabel } from "@/components/ui/field";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";

const TYPES = [
  { value: "bug", label: "Bug" },
  { value: "feature", label: "Feature request" },
  { value: "question", label: "Question" },
];

export function TypeField() {
  return (
    <Field>
      <FieldLabel>Type</FieldLabel>
      <Select items={TYPES} defaultValue="bug" name="type">
        <SelectTrigger>
          <SelectValue />
        </SelectTrigger>
        <SelectContent>
          {TYPES.map((t) => (
            <SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>
          ))}
        </SelectContent>
      </Select>
    </Field>
  );
}
```

### Vue

```vue
<script setup lang="ts">
import { NqSelect, NqSelectContent, NqSelectItem, NqSelectTrigger, NqSelectValue } from "@fadymondy/nasaq/vue";

const TYPES = [
  { value: "bug", label: "Bug" },
  { value: "feature", label: "Feature request" },
  { value: "question", label: "Question" },
];
</script>

<template>
  <div class="flex flex-col gap-1.5">
    <span class="text-label text-foreground">Type</span>
    <NqSelect default-value="bug" name="type">
      <NqSelectTrigger>
        <NqSelectValue />
      </NqSelectTrigger>
      <NqSelectContent>
        <NqSelectItem v-for="t in TYPES" :key="t.value" :value="t.value">{{ t.label }}</NqSelectItem>
      </NqSelectContent>
    </NqSelect>
  </div>
</template>
```

### Blade

```blade
<div class="flex flex-col gap-1.5">
    <span class="text-label text-foreground">Type</span>
    <x-nq::select name="type" value="bug">
        <x-nq::select.trigger>
            <x-nq::select.value />
        </x-nq::select.trigger>
        <x-nq::select.content>
            <x-nq::select.item value="bug">Bug</x-nq::select.item>
            <x-nq::select.item value="feature">Feature request</x-nq::select.item>
            <x-nq::select.item value="question">Question</x-nq::select.item>
        </x-nq::select.content>
    </x-nq::select>
</div>
```

### HTML + Alpine

```html
<div class="flex flex-col gap-1.5">
    <span class="text-label text-foreground">Type</span>
    <div data-slot="select" x-data="nqSelect('bug', false)" x-modelable="value" x-id="['nq-select']" class="contents">
    <button data-slot="select-trigger" x-ref="trigger" x-bind="trigger"
        class="flex h-control w-full min-w-0 items-center justify-between gap-2 rounded-control border border-input bg-card px-3 text-body text-foreground min-h-[var(--nq-touch-min,0px)] cursor-default select-none outline-none transition-colors duration-150 ease-nq focus-visible:border-nq-focus focus-visible:outline-1 focus-visible:outline-nq-focus data-popup-open:border-nq-focus data-invalid:border-nq-danger disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:text-[16px]">
    <span data-slot="select-value" x-text="label() ?? ''" :data-placeholder="empty() ? '' : undefined"
    class="min-w-0 flex-1 truncate text-start data-placeholder:text-muted-foreground"></span>
    <span class="flex shrink-0 text-muted-foreground [&_svg]:size-4"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="m7 15 5 5 5-5"/>
  <path d="m7 9 5-5 5 5"/>
</svg></span>
</button>
        <template x-teleport="body">
    <div data-slot="select-content" x-ref="popup" x-bind="popup" x-nq-presence="open" x-anchor.bottom-start.offset.4="$refs.trigger"
        class="z-50 min-w-[var(--anchor-width)] max-h-[var(--available-height)] overflow-y-auto rounded-floating border border-border bg-popover p-1.5 text-popover-foreground shadow-floating outline-none transition-opacity duration-150 ease-nq data-starting-style:opacity-0 data-ending-style:opacity-0">
        <div data-slot="select-item" x-bind="item('bug', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('bug')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">Bug</span>
</div>
            <div data-slot="select-item" x-bind="item('feature', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('feature')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">Feature request</span>
</div>
            <div data-slot="select-item" x-bind="item('question', false)"
    class="relative flex h-nav-row min-h-[var(--nq-touch-min,0px)] cursor-default select-none items-center gap-2.5 rounded-control ps-8 pe-2.5 text-body-sm text-foreground outline-none data-highlighted:bg-nq-selected data-disabled:pointer-events-none data-disabled:opacity-50">
    <span aria-hidden="true" class="absolute start-2.5 inline-flex size-4 items-center justify-center">
        <span x-show="isSelected('question')" x-cloak class="contents"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <path d="M20 6 9 17l-5-5"/>
</svg></span>
    </span>
    <span data-slot="select-item-text" class="min-w-0 flex-1 truncate">Question</span>
</div>
    </div>
</template>
            <template x-if="multiple">
            <template x-for="v in value" :key="v"><input type="hidden" name="type[]" :value="v"></template>
        </template>
        <template x-if="!multiple"><input type="hidden" name="type" :value="value ?? ''"></template>
    </div>
</div>
```
