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

## Паттерны

### Короткий выбор вида или режима

Когда использовать:

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

Заметки по композиции:

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

Компромиссы:

- Наглядное визуальное сравнение.
- Плохо подходит для длинных наборов вариантов.

```tsx
import {
  SegmentedControl,
  SegmentedControlIndicator,
  SegmentedControlItem,
  SegmentedControlItemInput,
} from '@vega-ui/react';

export default function ViewRangeSelector() {
  return (
    <SegmentedControl name='calendar-view' defaultValue='week' role='radiogroup' aria-label='Calendar view'>
      <SegmentedControlItem value='day'>
        <SegmentedControlItemInput />
        Day
      </SegmentedControlItem>
      <SegmentedControlItem value='week'>
        <SegmentedControlItemInput />
        Week
      </SegmentedControlItem>
      <SegmentedControlItem value='month'>
        <SegmentedControlItemInput />
        Month
      </SegmentedControlItem>
      <SegmentedControlItem value='year'>
        <SegmentedControlItemInput />
        Year
      </SegmentedControlItem>
      <SegmentedControlIndicator />
    </SegmentedControl>
  );
}
```

### Компактный выбор темы или плотности

Когда использовать:

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

Заметки по композиции:

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

Компромиссы:

- Компактно и выразительно.
- Более хрупко, чем группы радиокнопок, когда подписи или иконки неравномерны.

```tsx
import {
  Icon,
  SegmentedControl,
  SegmentedControlIndicator,
  SegmentedControlItem,
  SegmentedControlItemInput,
} from '@vega-ui/react';
import { MonitorIcon, MoonIcon, SunIcon } from '@vega-ui/icons';

export default function IconOnlyModeSelector() {
  return (
    <SegmentedControl name='appearance' defaultValue='system' size='sm' role='radiogroup' aria-label='Appearance'>
      <SegmentedControlItem value='light' title='Light'>
        <SegmentedControlItemInput aria-label='Light' />
        <Icon size='sm'><SunIcon /></Icon>
      </SegmentedControlItem>
      <SegmentedControlItem value='system' title='System'>
        <SegmentedControlItemInput aria-label='System' />
        <Icon size='sm'><MonitorIcon /></Icon>
      </SegmentedControlItem>
      <SegmentedControlItem value='dark' title='Dark'>
        <SegmentedControlItemInput aria-label='Dark' />
        <Icon size='sm'><MoonIcon /></Icon>
      </SegmentedControlItem>
      <SegmentedControlIndicator />
    </SegmentedControl>
  );
}
```

### Взаимоисключающий выбор внутри оверлеев

Когда использовать:

- Диалогу или выдвижной панели (Drawer) нужна одна короткая, всегда видимая группа выбора.

Заметки по композиции:

- Делайте контрол достаточно компактным для ширины оверлея.
- Не смешивайте слишком много длинных подписей в стеснённой раскладке.

Компромиссы:

- Хорошо работает в коротких формах внутри оверлеев.
- Перестаёт работать, если содержимое становится слишком широким или вариантов слишком много.

```tsx
import { CSSProperties, FormEvent, useState } from 'react';
import {
  Button,
  Dialog,
  DialogClose,
  DialogContent,
  DialogTrigger,
  Heading,
  Icon,
  IconButton,
  Label,
  SegmentedControl,
  SegmentedControlIndicator,
  SegmentedControlItem,
  SegmentedControlItemInput,
  Text,
} from '@vega-ui/react';
import { XIcon } from '@vega-ui/icons';

const densities = [
  { value: 'compact', label: 'Compact' },
  { value: 'default', label: 'Default' },
  { value: 'comfortable', label: 'Comfortable' },
];

export default function ExclusiveSelectorInsideOverlays() {
  const [density, setDensity] = useState('default');

  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    const data = new FormData(event.currentTarget);
    setDensity(String(data.get('density')));
  };

  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--spacing-6)', width: '100%' }}>
      <Text size={3}>Row density: <strong>{density}</strong></Text>
      <Dialog>
        <DialogTrigger asChild>
          <Button variant='secondary' appearance='ghost'>Display settings</Button>
        </DialogTrigger>
        <DialogContent aria-labelledby='display-settings-title'>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 'var(--spacing-8)' }}>
            <Heading id='display-settings-title' as='h2' size={4} fontWeight={600}>Display settings</Heading>
            <DialogClose asChild>
              <IconButton
                aria-label='Close'
                variant='secondary'
                appearance='ghost'
                style={{ flexShrink: 0, '--icon-button-size': '24px', '--icon-button-br-ratio': 0.5 } as CSSProperties}
              >
                <Icon size='xs'><XIcon strokeWidth={2.5} /></Icon>
              </IconButton>
            </DialogClose>
          </div>
          <form method='dialog' onSubmit={handleSubmit} style={{ display: 'grid', gap: 'var(--spacing-12)', marginBlockStart: 'var(--spacing-8)' }}>
            <div style={{ display: 'grid', gap: 'var(--spacing-3)' }}>
              <Label id='density-label'>Row density</Label>
              <SegmentedControl name='density' defaultValue={density} role='radiogroup' aria-labelledby='density-label'>
                {densities.map((item) => (
                  <SegmentedControlItem key={item.value} value={item.value}>
                    <SegmentedControlItemInput />
                    {item.label}
                  </SegmentedControlItem>
                ))}
                <SegmentedControlIndicator />
              </SegmentedControl>
            </div>
            <Button type='submit'>Apply</Button>
          </form>
        </DialogContent>
      </Dialog>
    </div>
  );
}
```

### С разделителями

Когда использовать:

- Между сегментами нужна видимая граница, например на малоконтрастной поверхности.

Заметки по композиции:

- Поставьте вертикальный `Separator` между элементами; он соседний элемент, а не часть сегмента.
- Задайте корню `display: flex`, чтобы разделители выровнялись с элементами.

Компромиссы:

- Границы между вариантами чётче.
- Разделители остаются видны и рядом с выбранным сегментом.

```tsx
import {
  SegmentedControl,
  SegmentedControlIndicator,
  SegmentedControlItem,
  SegmentedControlItemInput,
  Separator,
} from '@vega-ui/react';

export default function WithSeparators() {
  return (
    <SegmentedControl
      name='sort-order'
      defaultValue='newest'
      role='radiogroup'
      aria-label='Sort by'
      style={{ display: 'flex', width: 'min-content' }}
    >
      <SegmentedControlItem value='newest'>
        <SegmentedControlItemInput />
        Newest
      </SegmentedControlItem>
      <Separator orientation='vertical' style={{ height: '80%' }} />
      <SegmentedControlItem value='popular'>
        <SegmentedControlItemInput />
        Most popular
      </SegmentedControlItem>
      <Separator orientation='vertical' style={{ height: '80%' }} />
      <SegmentedControlItem value='price'>
        <SegmentedControlItemInput />
        Price
      </SegmentedControlItem>
      <SegmentedControlIndicator />
    </SegmentedControl>
  );
}
```

## Сравнение

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

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

### `SegmentedControl` или `Radio`

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

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

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

- достаточно более простой группы взаимоисключающего выбора в духе формы

Главный компромисс: сегментированный контрол нагляднее и компактнее, а радиокнопки проще и привычнее в формах.

### `SegmentedControl` или `Select`

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

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

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

- места мало или список вариантов длиннее

Главный компромисс: сегментированный контрол делает ставку на видимость, а селект — на плотность и масштабируемость.

### `SegmentedControl` или `Switch`

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

- пользователь выбирает один вариант из нескольких видимых состояний

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

- контрол — одиночный бинарный переключатель с мгновенным эффектом

Главный компромисс: сегментированный контрол — это взаимоисключающий выбор, а переключатель — смена бинарного состояния.

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

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

- варианты должны оставаться короткими, плотными и взаимоисключающими

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

- варианты богаче, похожи на карточки или выбираются независимо

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

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

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

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

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

### Индикатор не совпадает с выбранным элементом

#### Симптом

Подсвеченный индикатор смещён или имеет неверный размер.

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

Индикатор ищет свой якорь внутри ближайшего позиционированного предка. Кастомная обёртка с `position: relative/absolute` между корнем и элементами или переопределённый `position` корня ломают поиск якоря.

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

- Проверьте контрол с реалистичными подписями и шириной контейнера.
- Поищите позиционированные обёртки между `SegmentedControl` и его элементами.

#### Решение

- Оставляйте элементы прямыми потомками корня, без позиционированных обёрток.
- Перепроверьте кастомную ширину, разделители и динамическое изменение подписей.

### Контрол выглядит правильно, но не ведёт себя как группа радиокнопок

#### Симптом

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

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

`SegmentedControlItemInput` пропущен или `name` задан неправильно.

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

- Изучите отрендеренные инпуты.
- Убедитесь, что у всех элементов один и тот же `name`.

#### Решение

- Рендерите `SegmentedControlItemInput` внутри каждого элемента.
- Держите обязательный `name` одинаковым во всей группе.

### Подписи слишком неравномерны

#### Симптом

Контрол визуально разбалансирован, потому что одни сегменты намного длиннее других.

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

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

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

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

#### Решение

- По возможности сократите подписи.
- Перейдите на `Radio` или `Select`, когда набор вариантов уже плохо подходит для сегментированного контрола.
