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

## Паттерны

### Плотная матрица вариантов

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

Заметки о композиции:
Используйте `DataGridPickerItem` для каждой выбираемой ячейки и доверьте слою пикера стили активного и выбранного состояний.

Компромиссы:
Макет экономит место, но каждому элементу нужна высокая визуальная ясность, потому что подписи короткие.

```tsx
import {
  DataGridPicker,
  DataGridPickerItem,
  DataGridPickerRow,
  DataGridPickerRowGroup,
} from '@vega-ui/react';

const months = [
  ['Jan', 'Feb', 'Mar', 'Apr'],
  ['May', 'Jun', 'Jul', 'Aug'],
  ['Sep', 'Oct', 'Nov', 'Dec'],
];

export default function CompactMonthLikeMatrix() {
  return (
    <DataGridPicker
      aria-label='Statement month, 2026'
      size='sm'
      defaultSelected='2026-05'
      defaultActive='2026-05'
      style={{ width: 280 }}
    >
      <DataGridPickerRowGroup>
        {months.map((row, rowIndex) => (
          <DataGridPickerRow key={rowIndex} row={rowIndex}>
            {row.map((month, col) => {
              const value = `2026-${String(rowIndex * 4 + col + 1).padStart(2, '0')}`;

              return (
                <DataGridPickerItem key={month} col={col} value={value}>{month}</DataGridPickerItem>
              );
            })}
          </DataGridPickerRow>
        ))}
      </DataGridPickerRowGroup>
    </DataGridPicker>
  );
}
```

### Постраничный пикер

Когда использовать:
Набор вариантов настолько велик, что его стоит разбить на страницы по горизонтали.

Заметки о композиции:
Оберните сетку в `DataGridPickerScroller` и отрисуйте один компонент страницы внутри `DataGridPickerScrollerContent`. Страница берёт свой `index` из `useIndexedSnapScrollerContext`, вычисляет по нему данные и номера строк и передаёт его в `scope` своей группы строк.

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

```tsx
import { useRef, useState } from 'react';
import { ChevronLeft, ChevronRight } from '@vega-ui/icons';
import {
  DataGridPicker,
  DataGridPickerItem,
  DataGridPickerRow,
  DataGridPickerRowGroup,
  DataGridPickerScroller,
  DataGridPickerScrollerContent,
  Icon,
  IconButton,
  Text,
  useIndexedSnapScrollerContext,
  type IndexedSnapScrollerApiRef,
} from '@vega-ui/react';

const firstYear = 2016;
const rows = [0, 1, 2];
const cols = [0, 1, 2, 3];
const perPage = rows.length * cols.length;

const getPageStart = (page: number) => firstYear + page * perPage;

function YearPage() {
  const { index } = useIndexedSnapScrollerContext();

  return (
    <DataGridPickerRowGroup scope={index}>
      {rows.map((rowIndex) => {
        const row = index * rows.length + rowIndex;

        return (
          <DataGridPickerRow key={row} row={row}>
            {cols.map((col) => {
              const year = getPageStart(index) + rowIndex * cols.length + col;

              return <DataGridPickerItem key={year} col={col} value={year}>{year}</DataGridPickerItem>;
            })}
          </DataGridPickerRow>
        );
      })}
    </DataGridPickerRowGroup>
  );
}

export default function PagedPickerWithOwnButtons() {
  const scrollerRef = useRef<IndexedSnapScrollerApiRef>(null);
  const [page, setPage] = useState(0);

  return (
    <DataGridPicker aria-label='Graduation year' size='sm' defaultSelected={2021} defaultActive={2021} style={{ width: 280 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <IconButton aria-label='Previous years' size='sm' variant='secondary' appearance='transparent' onClick={() => scrollerRef.current?.prev()}>
          <Icon><ChevronLeft /></Icon>
        </IconButton>
        <Text size={2} aria-live='polite'>{getPageStart(page)}–{getPageStart(page) + perPage - 1}</Text>
        <IconButton aria-label='Next years' size='sm' variant='secondary' appearance='transparent' onClick={() => scrollerRef.current?.next()}>
          <Icon><ChevronRight /></Icon>
        </IconButton>
      </div>
      <DataGridPickerScroller apiRef={scrollerRef} defaultIndex={0} onScrollSnapChanging={(_, index) => setPage(index)}>
        <DataGridPickerScrollerContent style={{ width: '100%' }}>
          <YearPage />
        </DataGridPickerScrollerContent>
      </DataGridPickerScroller>
    </DataGridPicker>
  );
}
```

## Сравнение

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

Выбирайте `DataGridPicker`, когда интерфейсу нужен пикер в форме сетки с визуальной семантикой VegaUI. Выбирайте `DataGridSelectable`, когда важно поведение выбора, но визуальный слой должен быть полностью собственным.

### `DataGridPicker` и `DataGridSelectable`

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

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

### `DataGridPicker` и `MonthPicker`

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

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

### `DataGridPicker` и `DayPicker`

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

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

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

- интерфейсу нужна плотная выбираемая сетка
- размер и вариант должны следовать семантике пикеров VegaUI
- предметная область собственная, но по форме это всё ещё пикер

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

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

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

### Пикер прокручивается, но фокус восстанавливается не на той странице

#### Симптом

После перелистывания фокус перескакивает на неожиданный элемент или на первый элемент не той страницы.

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

Постраничные группы строк не задают стабильный `scope`, поэтому сетка не может отличить одну страницу от другой.

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

- изучите пропсы групп строк в постраничной композиции
- сравните поведение с явными значениями `scope` и без них

#### Решение

Задайте стабильный `scope` для каждой `DataGridPickerRowGroup` в макетах со скроллером.

### Выбранное и активное состояния трудно различить

#### Симптом

Пользователи могут фокусировать и выбирать элементы, но визуальные состояния выглядят слишком похоже.

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

Собственный рендер элементов скрывает семантику элемента пикера.

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

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

#### Решение

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

### Пикер ощущается как таблица, а не как пикер

#### Симптом

Технически интерфейс работает, но воспринимается как универсальная таблица данных, а не как элемент выбора.

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

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

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

- сравните рендер в продукте с готовыми историями пикера
- проверьте, отвечает ли `DataGridPickerItem` по-прежнему за поверхность выбора

#### Решение

Опирайтесь на слой элементов пикера или вернитесь к `DataGridSelectable`, если на самом деле нужна полностью собственная визуальная система.
