# Checkbox

A shadcn-style native checkbox primitive with full `ControlValueAccessor` support for `ngModel` and reactive forms.

## Import

```ts
import { CheckboxComponent } from '@edsis/component/checkbox';
import { LabelComponent } from '@edsis/component/label';
```

## Usage

Wrap the control and copy in `label[Label]` when the text should toggle the checkbox. This is the closest Angular equivalent to the upstream shadcn `Field` + `FieldLabel` composition.

```html
<label Label class="flex items-start gap-3">
  <Checkbox [(ngModel)]="accepted" name="terms-checkbox" class="mt-0.5" />
  <span class="grid gap-1 leading-none">
    <span>Accept terms and conditions</span>
    <span class="text-sm font-normal leading-5 text-muted-foreground">
      By clicking this checkbox, you agree to the terms.
    </span>
  </span>
</label>
```

Reactive forms continue to work through the component's value accessor.

```html
<Checkbox [formControl]="form.controls.marketingOptIn" aria-label="Receive product updates" />
```

## Common patterns

### Controlled checked state

Use `checkedChange` when the parent owns the signal and you want to keep the checkbox controlled without wrapping a form model.

```ts
const marketingOptIn = signal(false);
```

```html
<Checkbox
  [ngModel]="marketingOptIn()"
  (checkedChange)="marketingOptIn.set($event)"
  aria-label="Receive product updates"
/>
```

### Invalid state

Forward `aria-invalid="true"` to switch the control into the destructive invalid treatment.

```html
<label Label class="flex items-start gap-3 text-destructive">
  <Checkbox aria-invalid="true" name="terms-checkbox-invalid" class="mt-0.5" />
  <span class="grid gap-1 leading-none">
    <span>Accept terms and conditions</span>
    <span class="text-sm font-normal leading-5 text-muted-foreground">
      Choose this before continuing to checkout.
    </span>
  </span>
</label>
```

### Grouped preferences

Use a semantic `fieldset` for a checkbox list.

```html
<fieldset class="space-y-3">
  <legend class="text-sm font-semibold">Show these items on the desktop:</legend>
  @for (item of groupOptions; track item.id) {
  <label Label class="flex items-start gap-3">
    <Checkbox
      [ngModel]="desktopItems().has(item.id)"
      (checkedChange)="toggleDesktopItem(item.id, $event)"
      [name]="item.id"
      class="mt-0.5"
    />
    <span class="font-normal">{{ item.label }}</span>
  </label>
  }
</fieldset>
```

### Table row selection

Use `indeterminate` for the select-all checkbox and set `data-state="selected"` on the selected row so the table highlight stays in sync.

```html
<Checkbox
  [ngModel]="selectAll()"
  [indeterminate]="someRowsSelected()"
  aria-label="Select all team members"
  (checkedChange)="handleSelectAll($event)"
/>
```

## API reference

| Input              | Type                        | Default        | Notes                                                               |
| ------------------ | --------------------------- | -------------- | ------------------------------------------------------------------- |
| `id`               | `string`                    | auto-generated | Forwarded to the control; the native input id becomes `<id>-input`. |
| `name`             | `string \| null`            | `null`         | Forwarded to the native checkbox input.                             |
| `indeterminate`    | `boolean`                   | `false`        | Useful for select-all patterns.                                     |
| `required`         | `boolean`                   | `false`        | Forwarded to the native input.                                      |
| `aria-label`       | `string \| null`            | `null`         | Use for icon-only or table selection checkboxes.                    |
| `aria-describedby` | `string \| null`            | `null`         | Links helper or error text outside the checkbox.                    |
| `aria-labelledby`  | `string \| null`            | `null`         | Alternative to projected text or wrapper labels.                    |
| `aria-invalid`     | `boolean \| string \| null` | `null`         | Switches the control into the destructive invalid treatment.        |
| `class`            | `string`                    | `''`           | Adds utility classes to the wrapped Material checkbox host.         |

| Output          | Payload   |
| --------------- | --------- |
| `checkedChange` | `boolean` |

Public method: `focus()`.

## Styling and theming

The component renders native structural markup restyled through the library theme tokens.

- Default borders and icon color use the shared `input` and `primary` tokens.
- `aria-invalid="true"` switches the control to the `destructive` token set.
- Pass spacing or alignment utilities through the `class` input when the checkbox needs to align with multi-line content.

Extra visual adjustments belong in `checkbox.component.css` inside the library.

## Accessibility

- Always give the checkbox an accessible name via wrapper label text, projected content, `aria-label`, or `aria-labelledby`.
- Use `aria-describedby` to connect helper or error text rendered outside the checkbox.
- Use `indeterminate` for tri-state list headers and table select-all behavior.
- `required` and `name` are forwarded to the underlying native input.

## Keyboard interactions

- `Tab` moves focus to the checkbox in DOM order.
- `Space` toggles the checked state.

## Angular notes

- `ngModel`, reactive forms, and `checkedChange` all work with the same primitive.
- When you pass `id`, the control derives the native input id as `<id>-input`; wrapper labels or `aria-labelledby` are usually the simplest Angular pattern.
- Projected text still works if you prefer `<Checkbox>Accept</Checkbox>` for compact markup.

## Source parity

This Angular implementation follows the shadcn Checkbox examples while mapping the upstream `Field` helpers to Angular-native label wrappers, `fieldset` composition, semantic table rows, and signal-friendly state handling.
