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

## Паттерны

### Индикатор ёмкости

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

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

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

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

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

- Хорошо подходит для количественных сводок.
- Плохо подходит, когда диапазон ничего не говорит пользователям.

```tsx
import { Meter, MeterTrack, Text } from '@vega-ui/react';
import { useId } from 'react';

export default function StorageUsage() {
  const labelId = useId();
  const usedGb = 36.2;
  const limitGb = 50;
  const summary = `${usedGb} GB of ${limitGb} GB used`;

  return (
    <div style={{ display: 'grid', gap: 'var(--spacing-3)', width: '100%' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 'var(--spacing-8)' }}>
        <Text id={labelId} size={2} fontWeight={500}>Cloud storage</Text>
        <Text size={2} style={{ color: 'var(--label-secondary)', fontVariantNumeric: 'tabular-nums' }}>{summary}</Text>
      </div>
      <Meter value={usedGb} max={limitGb} valueText={summary} aria-labelledby={labelId} fullWidth>
        <MeterTrack />
      </Meter>
    </div>
  );
}
```

### Сводка состояния или оценки

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

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

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

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

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

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

```tsx
import { Card, Heading, Meter, MeterTrack, Text } from '@vega-ui/react';
import { useId } from 'react';

const usage = [
  { id: 'seats', label: 'Seats', used: 18, limit: 20, unit: 'seats' },
  { id: 'storage', label: 'Storage', used: 412, limit: 500, unit: 'GB' },
  { id: 'minutes', label: 'Build minutes', used: 1240, limit: 3000, unit: 'min' },
];

export const locales = ['en-US', 'ru-RU', 'ar-EG', 'he-IL'];

export default function MeterInsideACard({ locale = 'en-US' }: { locale?: string }) {
  const prefix = useId();
  const format = new Intl.NumberFormat(locale);

  return (
    <Card size='sm' style={{ display: 'grid', gap: 'var(--spacing-8)', width: '100%' }}>
      <Heading as='h3' size={4} fontWeight={600}>Pro plan usage</Heading>
      <ul style={{ display: 'grid', gap: 'var(--spacing-8)', margin: 0, padding: 0, listStyle: 'none' }}>
        {usage.map((item) => {
          const labelId = `${prefix}-${item.id}`;
          const summary = `${format.format(item.used)} of ${format.format(item.limit)} ${item.unit}`;

          return (
            <li key={item.id} style={{ display: 'grid', gap: 'var(--spacing-3)' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 'var(--spacing-8)' }}>
                <Text id={labelId} size={2}>{item.label}</Text>
                <Text size={2} style={{ color: 'var(--label-secondary)', fontVariantNumeric: 'tabular-nums' }}>{summary}</Text>
              </div>
              <Meter
                value={item.used}
                max={item.limit}
                low={item.limit * 0.75}
                high={item.limit * 0.9}
                optimum={0}
                valueText={summary}
                aria-labelledby={labelId}
                size='sm'
                fullWidth
              >
                <MeterTrack />
              </Meter>
            </li>
          );
        })}
      </ul>
    </Card>
  );
}
```

### Измерение с учётом порогов

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

- У диапазона есть хорошая, предупреждающая и плохая зоны.

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

- Настраивайте `low`, `high` и `optimum` осознанно.
- Не полагайтесь только на смену цвета для передачи смысла.

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

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

```tsx
import { Button, Meter, MeterTrack, Text } from '@vega-ui/react';
import { useId, useState } from 'react';

export default function BatteryLevel() {
  const labelId = useId();
  const statusId = useId();
  const [level, setLevel] = useState(64);

  let status = 'Battery is fine';
  if (level < 20) {
    status = 'Battery is low — connect the charger';
  } else if (level > 80) {
    status = 'Battery is almost full';
  }

  const discharge = () => setLevel(Math.max(level - 15, 0));
  const charge = () => setLevel(Math.min(level + 15, 100));

  return (
    <div style={{ display: 'grid', gap: 'var(--spacing-12)', width: '100%', maxWidth: 360 }}>
      <div style={{ display: 'grid', gap: 'var(--spacing-3)' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 'var(--spacing-8)' }}>
          <Text id={labelId} size={2} fontWeight={500}>Headphones battery</Text>
          <Text size={2} style={{ fontVariantNumeric: 'tabular-nums' }}>{level}%</Text>
        </div>
        <Meter
          value={level}
          max={100}
          low={20}
          high={80}
          optimum={100}
          valueText={`${level}%`}
          aria-labelledby={labelId}
          aria-describedby={statusId}
          fullWidth
        >
          <MeterTrack />
        </Meter>
        <Text id={statusId} size={2} style={{ color: 'var(--label-secondary)' }}>{status}</Text>
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--spacing-4)' }}>
        <Button size='sm' variant='secondary' appearance='ghost' onClick={discharge} disabled={level === 0}>
          Listen for an hour
        </Button>
        <Button size='sm' variant='secondary' appearance='ghost' onClick={charge} disabled={level === 100}>
          Charge for 15 minutes
        </Button>
      </div>
    </div>
  );
}
```

## Сравнение

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

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

### `Meter` или `Progress`

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

- значение — оценка, квота, ёмкость или другое ограниченное измерение

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

- значение отражает выполнение задачи во времени

Главный компромисс: meter описывает измерение, а progress — выполнение во времени.

### `Meter` или `Badge`

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

- интерфейсу нужна количественная визуализация диапазона

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

- интерфейсу нужна только короткая метка категории или статуса

Главный компромисс: meter визуализирует количество, а badge подписывает состояние.

### `Meter` или `MeterStack`

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

- одно непрерывное ограниченное значение — самое понятное представление

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

- должны оставаться видимыми несколько частей одного целого

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

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

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

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

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

### Вместо Meter, вероятно, нужен индикатор выполнения

#### Симптом

Значение на самом деле отражает текущее выполнение, а не ограниченное измерение.

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

`Meter` выбран для состояния прогресса во времени.

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

- Спросите себя, отражает ли число выполнение во времени.

#### Исправление

- Для выполнения задачи перейдите на `Progress`.

### Цвета порогов кажутся произвольными

#### Симптом

Полоса меняет цвет, но пользователи не понимают почему.

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

`low`, `high` и `optimum` настроены без достаточного пояснения вокруг.

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

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

#### Исправление

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

### Meter показывает значение, но не его контекст

#### Симптом

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

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

Компонент отрендерен без поясняющего текста вокруг.

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

- Уберите соседние заголовки и проверьте, остаётся ли смысл понятным.

#### Исправление

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