# CheckboxCard: Руководства

## Паттерны

### Плитка выбора тарифа

Когда использовать:
Показать возможности тарифа или варианты пакета, где кликабельной должна быть вся карточка.

Заметки по композиции:
Держите заголовок и описание в `CheckboxCardContent`, а контрол рендерите у противоположного края.

Компромиссы:
Паттерн выразительный, но занимает больше места, чем обычная строка с чекбоксом.

```tsx
import {
  CheckboxCard,
  CheckboxCardContent,
  CheckboxCardControl,
  CheckboxCardControlCheckedIcon,
  CheckboxCardControlIndeterminateIcon,
  CheckboxCardControlInput,
  CheckboxCardDescription,
  CheckboxCardTitle,
  CheckboxIndicator,
  Fieldset,
  FieldsetLegend,
} from '@vega-ui/react';

const addons = [
  { value: 'priority-support', title: 'Priority support', description: 'Replies within 1 business hour, 24/7 incident line.' },
  { value: 'audit-log', title: 'Audit log', description: 'Keep a 365-day history of every change in the workspace.' },
  { value: 'sso', title: 'Single sign-on', description: 'SAML and OIDC login through Okta, Entra ID or Google.' },
];

export default function PlanOptionTile() {
  return (
    <Fieldset style={{ width: '100%' }}>
      <FieldsetLegend style={{ marginBlockEnd: 'var(--spacing-5)' }}>Add-ons</FieldsetLegend>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--spacing-6)' }}>
        {addons.map((addon) => (
          <CheckboxCard key={addon.value}>
            <CheckboxCardContent>
              <CheckboxCardTitle>{addon.title}</CheckboxCardTitle>
              <CheckboxCardDescription>{addon.description}</CheckboxCardDescription>
            </CheckboxCardContent>
            <CheckboxCardControl>
              <CheckboxCardControlInput name='addons' value={addon.value} defaultChecked={addon.value === 'audit-log'} />
              <CheckboxIndicator>
                <CheckboxCardControlCheckedIcon />
                <CheckboxCardControlIndeterminateIcon />
              </CheckboxIndicator>
            </CheckboxCardControl>
          </CheckboxCard>
        ))}
      </div>
    </Fieldset>
  );
}
```

### Плитка настройки с участием в нативной форме

Когда использовать:
Приложению нужен богатый UI плитки, но отправка по-прежнему идёт через нативную форму.

Заметки по композиции:
Передайте всем `CheckboxCardControlInput` общий `name` и собственный `value`, а выбор читайте через `FormData.getAll`.

Компромиссы:
Интеграция с формой упрощается, но раскладка и семантика всё равно должны сохранять читаемость всей карточки.

```tsx
import { FormEvent, useState } from 'react';
import {
  Button,
  CheckboxCard,
  CheckboxCardContent,
  CheckboxCardControl,
  CheckboxCardControlCheckedIcon,
  CheckboxCardControlIndeterminateIcon,
  CheckboxCardControlInput,
  CheckboxCardDescription,
  CheckboxCardTitle,
  CheckboxIndicator,
  Fieldset,
  FieldsetLegend,
  Text,
} from '@vega-ui/react';

const contents = [
  { value: 'projects', title: 'Projects', description: 'Boards, tasks and their history' },
  { value: 'files', title: 'Files', description: 'Attachments up to 2 GB each' },
  { value: 'members', title: 'Members', description: 'Names, roles and last activity' },
];

export default function FormDataSubmission() {
  const [result, setResult] = useState('');

  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const included = new FormData(event.currentTarget).getAll('include');
    setResult(`include=${included.join(',')}`);
  };

  return (
    <form onSubmit={handleSubmit} onReset={() => setResult('')} style={{ display: 'grid', gap: 'var(--spacing-12)', width: '100%' }}>
      <Fieldset>
        <FieldsetLegend style={{ marginBlockEnd: 'var(--spacing-5)' }}>Export workspace data</FieldsetLegend>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--spacing-6)' }}>
          {contents.map((content) => (
            <CheckboxCard key={content.value} size='sm'>
              <CheckboxCardContent>
                <CheckboxCardTitle>{content.title}</CheckboxCardTitle>
                <CheckboxCardDescription>{content.description}</CheckboxCardDescription>
              </CheckboxCardContent>
              <CheckboxCardControl>
                <CheckboxCardControlInput name='include' value={content.value} defaultChecked />
                <CheckboxIndicator>
                  <CheckboxCardControlCheckedIcon />
                  <CheckboxCardControlIndeterminateIcon />
                </CheckboxIndicator>
              </CheckboxCardControl>
            </CheckboxCard>
          ))}
        </div>
      </Fieldset>
      <div style={{ display: 'grid', gap: 'var(--spacing-6)' }}>
        <div style={{ display: 'flex', gap: 'var(--spacing-4)' }}>
          <Button type='submit'>Start export</Button>
          <Button type='reset' variant='secondary' appearance='ghost'>Reset</Button>
        </div>
        <Text size={2} aria-live='polite' style={{ color: 'var(--label-secondary)' }}>{result}</Text>
      </div>
    </form>
  );
}
```

## Сравнение

### Быстрое правило выбора

Выбирайте `CheckboxCard`, когда каждому варианту-чекбоксу нужна подписанная поверхность размером с карточку. Выбирайте `Checkbox`, `Radio` или `SegmentedControl`, когда интерфейс может оставаться компактным.

### `CheckboxCard` или `Checkbox`

Используйте `CheckboxCard`, когда у варианта есть заголовок, описание или более богатая раскладка плитки.

Используйте `Checkbox`, когда достаточно компактной строчной подписи.

### `CheckboxCard` или `Radio`

Используйте `CheckboxCard` для независимых вариантов множественного выбора.

Используйте `Radio`, когда варианты группы взаимоисключающие.

### `CheckboxCard` или `Switch`

Используйте `CheckboxCard`, когда:

- можно независимо выбрать несколько вариантов с богатым содержимым

Используйте `Switch`, когда:

- одну бинарную настройку нужно сразу включать или выключать

### `CheckboxCard` или `SegmentedControl`

Используйте `CheckboxCard`, когда содержимое описательное и похоже на карточку.

Используйте `SegmentedControl`, когда варианты должны оставаться короткими, плотными и всегда видимыми.

### Выбирайте этот компонент, когда

- вся карточка должна быть целью выбора
- варианту нужны заголовок и описание
- по-прежнему действует семантика множественного выбора
- богатое содержимое варианта важнее плотности

### Не выбирайте этот компонент, когда

- в группе выбирается один вариант
- содержимое варианта достаточно короткое для обычного чекбокса
- интерфейсу нужны плотные контролы, а не плитки

## Решение проблем

### Кликабельным кажется только крошечный контрол

#### Симптом

Пользователи кликают по телу карточки, но вариант не ощущается как контрол на всю карточку.

#### Вероятная причина

Собственная композиция или стили сломали подписанную поверхность карточки, и интерактивным выглядит только маленький контрол.

#### Как проверить

- проверьте взаимодействие указателем по всей карточке
- убедитесь, что корень по-прежнему рендерится как подписанная область клика

#### Решение

Сохраняйте композицию «карточка-подпись» в корне и избегайте переопределений pointer-events, которые изолируют контрол.

### Карточка не участвует в нативной отправке формы

#### Симптом

Отмеченное состояние визуально меняется, но ожидаемого значения нет в `FormData`.

#### Вероятная причина

Отсутствует `CheckboxCardControlInput`.

#### Как проверить

- отправьте форму
- изучите `FormData`

#### Решение

Рендерите `CheckboxCardControlInput` внутри области контрола.

### Неопределённое состояние не появляется

#### Симптом

Интерфейс должен показывать смешанное состояние, но карточка выглядит только отмеченной или неотмеченной.

#### Вероятная причина

`indeterminate` не передан, или собственный рендер контрола пропустил ветку с иконкой неопределённого состояния.

#### Как проверить

- изучите пропсы корня
- сравните собственный контрол с готовыми stories

#### Решение

Передайте `indeterminate` и оставьте `CheckboxCardControlIndeterminateIcon` в композиции контрола, когда это состояние важно.
