GrBadgeWrap

  • ядро
  • Обратная связь
  • API · 8 props

Берут, когда счётчик относится к контролу.

import { GrBadgeWrap } from '@feugene/granularity/components/GrBadgeWrap'

Обзор

Когда брать

  • счётчик относится к контролу — непрочитанные на иконке почты, товары в корзине, задачи на вкладке;
  • важен сам факт, а не число — dot показывает точку без цифры;
  • число может быть большим — max сворачивает его в «99+»;
  • ноль надо показать — showZero; по умолчанию счётчик исчезает, а не рисует «0».

Когда взять другое

НужноБерите
Метка стоит в потоке, а не поверх контролаGrBadge
Статус присутствия на аватареGrAvatar
Счётчик — часть вкладкиGrTabs с badge
Счётчик — часть раздела навигацииGrBottomNav с badge

Варианты все тона и виды — настоящим компонентом

tone

  • primary
  • neutral
  • success
  • warning
  • danger
  • info
  • slate
  • azure

Playground контролы выводятся из API, а не пишутся руками

Сцена

Загружается…

0 изменено· состояние — в адресе

Примеры 4 у каждого — живое демо и его код

  • Шапка приложения: сколько и насколько срочно

    Срочность несёт тон: красный оставлен тревогам, которые требуют действия сегодня, а длинная очередь сообщений — спокойный primary. max сворачивает 128 в «99+», а aria-label сохраняет для скринридера настоящее число. Факт без числа — dot, и на круглом аватаре точка сама садится на окружность.

    Acme Billing
    128 unread messages3 alerts need actionALBilling details are missing
  • Новые сообщения и упоминания — два угла одной плитки

    У пространства с упоминаниями обычно есть и новые сообщения, и панель показывает оба: точка сверху говорит «что-то случилось», число снизу — placement="bottom-right" — сколько раз позвали вас. Откройте пространство, и метки переходят в список каналов: там счётчик стоит в строке, и это уже GrBadge.

    Acme Billing
  • Счётчик, который вздрагивает только от новостей

    animate отмечает то, чего покупатель ещё не знает: появление счётчика и рост числа. Убрать товар — его собственное действие, и число уменьшается молча, а на нуле счётчик вовсе уходит с иконки. Спокойный тон neutral подходит корзине — тревоги в ней нет.

    Northwind Office1 items in the cart
    Office chair, oak€349 · ships in 3 days
    In the cart: €349
  • Ноль — это ответ

    Отчёты, сохранённые без связи, ждут в исходящих. По умолчанию ноль убирает счётчик, но здесь «отправлять нечего» — ровно то, что инспектор проверяет перед уходом с объекта: show-zero оставляет его, а тон меняется с warning на success. Синхронизация опустошает очередь без «попа», новый отчёт возвращает его.

    Bremen yardReport #2041 Outbox 2 reports waiting to be sent

    2 waiting · last sync 09:40

API порождается из пакета · разойтись не может

Props

PropTypeпо умолчаниюОписание
placement"top-right" | "top-left" | "bottom-right" | "bottom-left" | undefined"top-right"Угол, в котором висит метка. По умолчанию `top-right`. Сдвиг от угла — переменные `--gr-badge-wrap-offset-x/-y`.
tone
"primary" | "neutral" | "success" | "warning" | "danger" | ""primary" | "neutral" | "success" | "warning" | "danger" | "info" | "slate" | "azure" | undefined
"danger"Цвет метки из шкалы тонов: заливка — роль тона, текст — её `-fg`. По умолчанию `danger`.
ariaLabelstring | undefinedundefinedПодпись счётчика для скринридера. Не задана — берётся из локали.
valuestring | number | undefinedundefinedЗначение счётчика: число или короткая строка («NEW»). Не задано — счётчика нет; ноль скрыт, пока не включён `showZero`.
dotboolean | undefinedfalseТочка вместо числа: чистая декорация, если не задан `ariaLabel`.
maxnumber | undefinedundefinedПорог: значения больше него рисуются как «{max}+».
showZeroboolean | undefinedfalseПоказывать ли нулевое значение. По умолчанию ноль скрыт.
animateboolean | undefinedfalseОтмечать ли появление и рост **счётчика** анимацией. Точке анимировать нечего.

Слоты

SlotTypeОписание
defaultanyЭлемент, к которому крепится метка.

Типы

Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.

Импорт
import type { GrBadgeWrapProps, GrBadgeWrapTone, GrBadgeWrapPlacement } from '@feugene/granularity/components/GrBadgeWrap'

Файлdist/types/components/GrBadgeWrap/GrBadgeWrap.vue.d.ts

GrBadgeWrapPropsinterface

GrBadgeWrap — обёртка, вешающая на потомка счётчик или точку-индикатор. Число объявляется скринридеру: «3» без единицы измерения ничего не значит, поэтому рядом с бейджем идёт визуально скрытая подпись.

interface GrBadgeWrapProps {
    /**
     * Значение счётчика: число или короткая строка («NEW»). Не задано — счётчика
     * нет; ноль скрыт, пока не включён `showZero`.
     */
    value?: string | number;
    /** Точка вместо числа: чистая декорация, если не задан `ariaLabel`. */
    dot?: boolean;
    /** Порог: значения больше него рисуются как «{max}+». */
    max?: number;
    /** Показывать ли нулевое значение. По умолчанию ноль скрыт. */
    showZero?: boolean;
    /** Цвет метки из шкалы тонов: заливка — роль тона, текст — её `-fg`. По умолчанию `danger`. */
    tone?: GrBadgeWrapTone;
    /**
     * Угол, в котором висит метка. По умолчанию `top-right`. Сдвиг от угла —
     * переменные `--gr-badge-wrap-offset-x/-y`.
     */
    placement?: GrBadgeWrapPlacement;
    /** Подпись счётчика для скринридера. Не задана — берётся из локали. */
    ariaLabel?: string;
    /** Отмечать ли появление и рост **счётчика** анимацией. Точке анимировать нечего. */
    animate?: boolean;
}

Файлdist/types/components/GrBadgeWrap/config.d.ts

grBadgeWrapConfigconst
const grBadgeWrapConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrBadgeWrap">;

Файлdist/types/components/GrBadgeWrap/grBadgeWrapStyles.d.ts

GrBadgeWrapTonetype
type GrBadgeWrapTone = GrTone;
GrBadgeWrapPlacementtype
type GrBadgeWrapPlacement = typeof GR_BADGE_WRAP_PLACEMENTS[number];
formatBadgeValuefunction

Видимый текст счётчика: число больше порога сворачивается в «{max}+», иначе бейдж расползается и ломает соседей. Озвучивается при этом настоящее значение — «99 плюс» пользователю ничего не говорит.

function formatBadgeValue(value: string | number, max?: number): string;

Файлdist/types/components/GrBadgeWrap/safelist.d.ts

grBadgeWrapSafelistconst
const grBadgeWrapSafelist: string[];

Использование из прозы библиотеки

Счётчик
vue
<GrBadgeWrap :value="3">
  <GrButton size="sm" variant="outline">Inbox</GrButton>
</GrBadgeWrap>

Число рисуется декоративно, а рядом идёт визуально скрытая подпись — иначе диктор читал бы «Inbox, 3» без единицы измерения либо не читал счётчик вовсе. Текст берётся из локали (gr.badgeWrap.count), ariaLabel перебивает его.

Ноль по умолчанию не показывается: пустой кружок на иконке читается как ошибка. showZero возвращает его, когда ноль — осмысленное состояние («0 черновиков»).

Обрезка крупных чисел
vue
<GrBadgeWrap :value="120" :max="99">
  <GrButton size="sm" variant="outline">Inbox</GrButton>
</GrBadgeWrap>

Видно «99+», а озвучивается настоящее число: «120 непрочитанных». «99 плюс» пользователю ничего не сообщает.

Функция доступна отдельно: import { formatBadgeValue } from '@feugene/granularity'.

Точка
vue
<GrBadgeWrap dot aria-label="Есть непрочитанные">
  <GrAvatar :size="40">AD</GrAvatar>
</GrBadgeWrap>

Точка не несёт числа и остаётся декоративной, пока ей не задали ariaLabel, — тогда факт события объявляется словом. Годится, когда важно «требует внимания», а не «сколько именно».

Ребёнок обязателен. Метка позиционируется абсолютно относительно корня, а корень без содержимого имеет нулевой размер: точка нарисуется вокруг пустого места, не займёт в раскладке ничего и налезет на соседей. Нужен статус-маркер сам по себе, в потоке, — это dot у GrBadge.

Анимация счётчика
vue
<GrBadgeWrap :value="unread" animate>
  <GrButton size="sm" variant="outline">Inbox</GrButton>
</GrBadgeWrap>

animate отмечает «попом» то, о чём пользователь ещё не знает: появление счётчика и рост числа. Убыль — след его собственного действия, и подсвечивать её значит мигать бейджем на каждом прочитанном письме.

ПереходЧто происходит
счётчика не было → появился«поп»
число выросло«поп»
число уменьшилосьмолча
счётчик исчезмолча
первый рендер страницымолча

Строковое значение (value="NEW") порядка не имеет, поэтому любая его смена считается событием.

Пачка изменений даёт один «поп»: пока анимация играет, новое значение её не перезапускает. Пять писем, пришедших за секунду, отмечаются одним движением, а не мельтешением.

По умолчанию animate выключен. Уместна ли анимация, знает приложение: счётчик, который меняется раз в час, и счётчик, который меняется постоянно, — разные случаи, и библиотека выбирает за потребителя тихий.

Точка (dot) в анимации не участвует: у неё нет значения, и «рост» для неё не определён.

Движение подчиняется prefers-reduced-motion через глобальный кламп пакета — подробности в motion.md. Длительность и кривая берутся из --gr-duration-base и --gr-ease-out, то есть настраиваются темой вместе со всем остальным движением.

Тон и положение
ПропЗначенияПо умолчанию
toneвся шкала GR_TONES (neutral, primary, success, warning, danger, info, slate, azure)danger
placementtop-right | top-left | bottom-right | bottom-lefttop-right

Цвет текста берётся из парного токена --gr-<tone>-fg, а не из белого литерала: бейдж переживает смену темы.

Смещение от угла настраивается переменными — темой или инлайн-стилем:

vue
<GrBadgeWrap :value="3" style="--gr-badge-wrap-offset-x: -0.75rem" />
ПеременнаяПо умолчанию (счётчик / точка)
--gr-badge-wrap-offset-x-0.5rem / 0
--gr-badge-wrap-offset-y-0.5rem / 0

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

Почему не GrBadge внутри

Обёртка рисует свой минимальный бейдж намеренно: иначе каждый счётчик тянул бы за собой полноразмерный компонент со своей шкалой размеров и радиусов ради кружка в 20 пикселей.