# Radio Group

Accessible exclusive-choice control composed from `RadioGroup` and `Radio`.

## Import

```ts
import {
  FormContentComponent,
  FormDescriptionComponent,
  FormFieldComponent,
  FormFieldsetComponent,
  FormLegendComponent,
  FormTitleComponent,
} from '@edsis/component/form';
import { LabelComponent } from '@edsis/component/label';
import { RadioComponent, RadioGroupComponent } from '@edsis/component/radio';
```

## Composition

The upstream shadcn `RadioGroup` examples map to the existing Angular radio and form primitives.

```text
RadioGroup
├── label[Label]
│   ├── Radio
│   └── FormContent
│       ├── FormTitle
│       └── p[FormDescription]
└── label[Label]
    ├── Radio
    └── copy or FormContent
```

## Basic Usage

Use `RadioGroup` as the value owner and wrap each option in `label[Label]` when the whole row should toggle the radio.

```html
<RadioGroup name="density" [(ngModel)]="density" class="w-fit">
  <label Label class="flex items-center gap-3">
    <Radio value="default" />
    <span>Default</span>
  </label>
  <label Label class="flex items-center gap-3">
    <Radio value="comfortable" />
    <span>Comfortable</span>
  </label>
  <label Label class="flex items-center gap-3">
    <Radio value="compact" />
    <span>Compact</span>
  </label>
</RadioGroup>
```

Reactive forms also work because the group implements `ControlValueAccessor`.

## Common Patterns

### Descriptive rows

Map the shadcn `Field` and `FieldContent` helpers to `FormField` and `FormContent`.

```html
<RadioGroup name="density" [(ngModel)]="density" class="w-full max-w-md">
  <label Label class="block rounded-xl border border-border bg-card/40 p-4">
    <FormField orientation="horizontal" class="gap-4">
      <Radio value="default" />
      <FormContent>
        <FormTitle>Default</FormTitle>
        <p FormDescription>Standard spacing for most use cases.</p>
      </FormContent>
    </FormField>
  </label>
</RadioGroup>
```

### Choice cards

Wrap each plan card in `label[Label]` so the full surface toggles the choice.

```html
<RadioGroup name="plan" [(ngModel)]="selectedPlan" class="w-full max-w-md">
  <label Label class="block rounded-xl border border-border bg-card/40 p-4">
    <FormField orientation="horizontal" class="items-start gap-4">
      <FormContent>
        <FormTitle>Pro</FormTitle>
        <p FormDescription>For growing businesses.</p>
      </FormContent>
      <Radio value="pro" />
    </FormField>
  </label>
</RadioGroup>
```

### Semantic fieldsets

Use a real fieldset and legend when the radio list answers one grouped question.

```html
<fieldset FormFieldset class="max-w-md rounded-xl border border-border bg-card/40 p-5">
  <legend FormLegend variant="label">Subscription plan</legend>
  <p class="text-sm leading-6 text-muted-foreground">
    Yearly and lifetime plans offer significant savings.
  </p>

  <RadioGroup name="subscription-plan" [(ngModel)]="subscriptionPlan" class="mt-4">
    <label Label class="block rounded-lg border border-border/70 bg-background/80 p-3">
      <FormField orientation="horizontal" class="gap-4">
        <Radio value="monthly" />
        <FormContent>
          <FormTitle>Monthly ($9.99/month)</FormTitle>
        </FormContent>
      </FormField>
    </label>
  </RadioGroup>
</fieldset>
```

### Validation and helper text

Forward helper and error relationships from the group when the descriptive copy sits outside the individual items.

```html
<FormField invalid class="max-w-md rounded-xl border border-destructive/40 bg-destructive/5 p-5">
  <FormTitle>Notification Preferences</FormTitle>
  <p id="notification-help" class="text-sm leading-6 text-muted-foreground">
    Choose how you want to receive notifications.
  </p>

  <RadioGroup
    name="notification-preferences"
    [(ngModel)]="notificationPreference"
    aria-describedby="notification-help notification-error"
    aria-invalid="true"
  >
    <label Label class="block rounded-lg border border-destructive/40 bg-background/80 p-3">
      <FormField orientation="horizontal" class="gap-4">
        <Radio value="email" />
        <FormContent>
          <FormTitle>Email only</FormTitle>
        </FormContent>
      </FormField>
    </label>
  </RadioGroup>

  <p id="notification-error" class="text-sm font-medium text-destructive">
    Select one delivery channel before saving notification settings.
  </p>
</FormField>
```

## API Reference

### `RadioGroupComponent`

| Input              | Type                         | Default      | Notes                                                        |
| ------------------ | ---------------------------- | ------------ | ------------------------------------------------------------ |
| `name`             | `string`                     | `''`         | Forwarded to the underlying Material radio group.            |
| `orientation`      | `'horizontal' \| 'vertical'` | `'vertical'` | Switches between stacked rows and inline columns.            |
| `aria-label`       | `string \| null`             | `null`       | Use when the group needs an explicit accessible name.        |
| `aria-labelledby`  | `string \| null`             | `null`       | Alternative to `aria-label` for external group headings.     |
| `aria-describedby` | `string \| null`             | `null`       | Connects helper or error text outside the group.             |
| `aria-invalid`     | `boolean \| string \| null`  | `null`       | Marks the group invalid for validation-driven presentations. |
| `class`            | `string`                     | `''`         | Adds utility classes to the wrapped group host.              |

| Output        | Payload  |
| ------------- | -------- |
| `valueChange` | `string` |

Public method: `focus()`.

### `RadioComponent`

| Input      | Type      | Default |
| ---------- | --------- | ------- |
| `value`    | `string`  | —       |
| `disabled` | `boolean` | `false` |
| `class`    | `string`  | `''`    |

## Styling and Theming

The radio primitives render native structural radio markup restyled through the shared theme tokens.

- The selected dot uses the `primary` token.
- Unselected outlines use the `input` token rather than current text color.
- The group uses CSS grid; horizontal orientation flips to `grid-flow-col auto-cols-max`.
- Card-style layouts and invalid treatments are best handled by surrounding `label[Label]` and `FormField` composition.

## Accessibility

- Use a real `fieldset` and `legend` when the radio list answers one grouped question.
- Wrap each option in `label[Label]` when the whole row or card should toggle the control.
- Use `aria-describedby` on `RadioGroup` to connect helper or error text rendered outside the option rows.
- Use `aria-invalid` on `RadioGroup` when validation fails and the group should be announced as invalid.

## Keyboard Interactions

- `Tab` moves focus to the selected item or first enabled item in the group.
- Arrow keys move between options using standard radio-group behavior.
- `Space` selects the focused option.

## Angular Notes

- `RadioGroup` owns the selected value and works with both `ngModel` and reactive forms.
- `Radio` represents only the selectable item; visible copy usually lives beside it in a wrapper label or `FormContent` block.
- The shadcn `Field` family maps to the existing `form` entrypoint rather than a separate radio-specific helper package.

## Source Parity

This Angular implementation follows the same high-value shadcn radio-group examples: descriptive rows, choice cards, semantic fieldsets, disabled states, invalid messaging, and RTL. The deliberate difference is the use of the existing `form` and `label` primitives instead of introducing a separate runtime `Field` API.
