# ToggleGroup

> Pressed-state buttons grouped as a segmented control, single or multiple selection, plus a standalone Toggle.

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

## Install

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

A row of buttons that each hold a pressed state. With the default single selection it is a segmented control
(list / grid view, text alignment). With `multiple` several items can be pressed at once (bold, italic). `Toggle`
alone is a single pressed-state button (star, pin). Built on Base UI `Toggle` and `ToggleGroup`.

## When to use

- Choosing a view mode, alignment or density from two to five short options.
- Formatting toolbars where each option is independent (`multiple`).

## When not to use

- Switching between panels of content: use `Tabs`.
- A choice that is submitted with a form and needs descriptions: use `RadioGroup`.
- Filtering by category with many chips: use `ChipGroup`.
- An immediate on/off setting: use `Switch`.

## Import

```tsx
import { Toggle, ToggleGroup } from "@fadymondy/nasaq/web";
```

## Quick start

```tsx
import { Toggle, ToggleGroup } from "@fadymondy/nasaq/web";

export function ViewMode() {
  return (
    <ToggleGroup defaultValue={["list"]} aria-label="View mode">
      <Toggle value="list">List</Toggle>
      <Toggle value="grid">Grid</Toggle>
    </ToggleGroup>
  );
}
```

## Anatomy

```
<ToggleGroup>   data-slot="toggle-group"   data-variant="segmented | outline"
└─ <Toggle>     data-slot="toggle"         data-pressed when on
```

## API

### ToggleGroup (`ToggleGroupProps`)

All Base UI `ToggleGroup` props, plus:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"segmented" \| "outline"` | `"segmented"` | Tinted track with a raised pressed item, or joined bordered buttons. |
| `multiple` | `boolean` | `false` | Allow several pressed items. |
| `value` / `defaultValue` | `readonly string[]` | none | Values of the pressed items. Always an array, even in single mode. |
| `onValueChange` | `(value: string[], eventDetails) => void` | none | Called when the pressed set changes. |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Arrow key axis. |
| `loopFocus` | `boolean` | `true` | Wrap focus at the ends. |
| `disabled` | `boolean` | `false` | Disable every item. |

### Toggle

All Base UI `Toggle` props: `value` (its id inside a group), `pressed` / `defaultPressed`, `onPressedChange`, `disabled`.
Outside a group it renders as an outline button.

## Examples

Multiple selection with icons (icon-only items need `aria-label`):

```tsx
import { Toggle, ToggleGroup } from "@fadymondy/nasaq/web";
import { Bold, Italic } from "lucide-react";

export function Format() {
  return (
    <ToggleGroup multiple variant="outline" aria-label="Formatting">
      <Toggle value="bold" aria-label="Bold"><Bold /></Toggle>
      <Toggle value="italic" aria-label="Italic"><Italic /></Toggle>
    </ToggleGroup>
  );
}
```

Controlled, in Arabic:

```tsx
import { useState } from "react";
import { Toggle, ToggleGroup } from "@fadymondy/nasaq/web";

export function ViewModeAr() {
  const [value, setValue] = useState<string[]>(["list"]);
  return (
    <ToggleGroup value={value} onValueChange={setValue} aria-label="طريقة العرض">
      <Toggle value="list">قائمة</Toggle>
      <Toggle value="grid">شبكة</Toggle>
    </ToggleGroup>
  );
}
```

## Accessibility

| Key | Action |
| --- | --- |
| Tab | Moves focus into the group, then out. |
| Arrow keys | Move between items. In RTL, ArrowLeft moves to the next item. |
| Space / Enter | Toggles the focused item. |

- The group has `role="group"`; items expose `aria-pressed`. Name the group with `aria-label` and localise it.
- In single mode, pressing the pressed item again unpresses it and gives an empty array; guard in `onValueChange` if one must always stay selected.

## RTL & i18n

Items are laid out in reading order, so the first item sits at the inline start. The outline variant rounds the
inline-start and inline-end corners with logical classes.

## Styling & tokens

`bg-secondary` track, `bg-card` pressed item, `bg-nq-selected` and `border-primary` for pressed outline items,
`text-muted-foreground` idle text, focus ring `nq-focus`. State attributes: `data-pressed`, `data-disabled`.
`className` merges onto both parts.

## Do / Don't

- Do keep labels to one or two words.
- Don't use it for page navigation.

## Related

[tabs](https://docs.nasaqui.com/components/tabs), [radio-group](https://docs.nasaqui.com/components/radio-group), [chip-group](https://docs.nasaqui.com/components/chip-group), [button](https://docs.nasaqui.com/components/button).

## Lab

https://docs.nasaqui.com/?path=/docs/components-actions-toggle-group--docs

## Code

### React

```tsx
import { Toggle, ToggleGroup } from "@fadymondy/nasaq/web";

export function ViewMode() {
  return (
    <ToggleGroup defaultValue={["list"]} aria-label="View mode">
      <Toggle value="list">List</Toggle>
      <Toggle value="grid">Grid</Toggle>
    </ToggleGroup>
  );
}
```

### shadcn

```tsx
import { Toggle, ToggleGroup } from "@/components/ui/toggle-group";

export function ViewMode() {
  return (
    <ToggleGroup defaultValue={["list"]} aria-label="View mode">
      <Toggle value="list">List</Toggle>
      <Toggle value="grid">Grid</Toggle>
    </ToggleGroup>
  );
}
```

### Vue

```vue
<script setup lang="ts">
import { NqToggle, NqToggleGroup } from "@fadymondy/nasaq/vue";
</script>

<template>
  <NqToggleGroup :default-value="['list']" aria-label="View mode">
    <NqToggle value="list">List</NqToggle>
    <NqToggle value="grid">Grid</NqToggle>
  </NqToggleGroup>
</template>
```

### Blade

```blade
<x-nq::toggle-group :default-value="['list']" aria-label="View mode">
    <x-nq::toggle-group.toggle value="list">List</x-nq::toggle-group.toggle>
    <x-nq::toggle-group.toggle value="grid">Grid</x-nq::toggle-group.toggle>
</x-nq::toggle-group>
```

### HTML + Alpine

```html
<div role="group" data-slot="toggle-group" data-variant="segmented" data-orientation="horizontal"
    x-data="nqToggleGroup(JSON.parse('[\u0022list\u0022]'), false)" x-modelable="value" x-bind="root"
            aria-label="View mode" class="flex w-fit max-w-full gap-0.5 rounded-control bg-secondary p-0.5">
    <button type="button" data-slot="toggle" aria-pressed="true"
     x-bind="toggle('list')"      data-pressed         class="inline-flex h-7 shrink-0 items-center justify-center gap-1.5 whitespace-nowrap px-3 text-label text-muted-foreground outline-none transition-colors duration-150 ease-nq hover:text-foreground [&_svg]:size-4 [&_svg]:shrink-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus data-disabled:pointer-events-none data-disabled:opacity-50 rounded-[calc(var(--radius-control)-2px)] data-pressed:bg-card data-pressed:text-foreground data-pressed:shadow-xs">List</button>
    <button type="button" data-slot="toggle" aria-pressed="false"
     x-bind="toggle('grid')"             class="inline-flex h-7 shrink-0 items-center justify-center gap-1.5 whitespace-nowrap px-3 text-label text-muted-foreground outline-none transition-colors duration-150 ease-nq hover:text-foreground [&_svg]:size-4 [&_svg]:shrink-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-nq-focus data-disabled:pointer-events-none data-disabled:opacity-50 rounded-[calc(var(--radius-control)-2px)] data-pressed:bg-card data-pressed:text-foreground data-pressed:shadow-xs">Grid</button>
</div>
```
