GrAvatar
Берут, когда человека или сущность узнают по картинке.
Когда брать
- человека или сущность узнают по картинке — список участников, автор комментария, владелец задачи;
- картинки может не быть — откат идёт по цепочке:
src,fallbackSrc, инициалы изname, слот; - нужен статус присутствия — точка на аватаре вместо отдельного значка рядом;
- участников несколько —
GrAvatarGroupсобирает их в ряд с наложением и счётчиком.
Когда взять другое
| Нужно | Берите |
|---|---|
| Изображение нужно рассмотреть | GrImageViewer |
| Показывается иконка, а не человек | GrIcon |
| Нужен счётчик поверх контрола | GrBadgeWrap |
| Статус текстом | GrBadge |
Что показывается
Порядок отката: src → fallbackSrc → инициалы из name → слот по умолчанию.
<GrAvatar src="https://cdn.example/u/42.png" fallback-src="/avatar.svg" name="Ada Lovelace" />
Битая ссылка обрабатывается: по @error компонент переходит к следующему
варианту, и браузер не рисует иконку сломанного изображения. Смена src
сбрасывает ошибку — новая ссылка не наследует судьбу прошлой.
Пока картинка едет, её место держит скелет: иначе ряд аватаров мигает пустыми кружками.
Имя и инициалы
name даёт инициалы (первые буквы двух первых слов) и доступное имя. Когда есть
картинка, имя несёт её alt; без картинки роль img и имя берёт на себя сам
аватар — иначе кружок с инициалами для диктора пуст. alt сильнее name, слот
сильнее инициалов.
Функция инициалов доступна отдельно: import { initialsFrom } from '@feugene/granularity'.
Кегль считается от диаметра — треть, но не меньше 10px. Размер аватара бывает
произвольным числом пикселей, поэтому карта по ступеням шкалы его не закрывает.
Правило одно на инициалы, слотовый текст и счётчик «+N» у GrAvatarGroup: они
стоят в одном ряду, и разные пропорции читались бы как сбой.
Статус
<GrAvatar name="Ada Lovelace" status="online" />
online | offline | busy | away. Точка декоративна (aria-hidden), а статус
объявляется словом из локали (gr.avatar.status.*): цвет сам по себе смысл не
передаёт.
Ряд участников
<GrAvatarGroup :max="3" :total="9" aria-label="Команда релиза">
<GrAvatar v-for="member in team" :key="member.id" :name="member.name" />
</GrAvatarGroup>
max ограничивает число видимых аватаров, остаток сворачивается в «+N»; total
нужен, когда в слоте лежит лишь часть участников. Группа объявлена
role="group", а её имя содержит и подпись, и число скрытых — диктор слышит
«Команда релиза, и ещё 6», а не набор безымянных картинок.
size и shape группы доходят до детей через контекст, чтобы ряд не расползался.
Размер и форма
size принимает ступень шкалы (xs…lg, читается из GrConfigProvider) или
число пикселей — у аватара исторически произвольный диаметр. shape
(circle | square) тоже настраивается глобально через
componentDefaults.GrAvatar.shape.
Playground 5
Загружается…
<GrAvatar />Установка
npm i @feugene/granularityИмпорт
import { GrAvatar } from '@feugene/granularity/components/GrAvatar'API
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
size | GrSizeWithPx | undefined | undefined | Размер по канонической шкале (`xs|sm|md|lg`) — тогда работает `GrConfigProvider`. Число — escape-hatch: аватар нужен произвольного диаметра (24px в строке списка, 96px в профиле), и четыре ступени шкалы этого не закрывают. |
name | string | undefined | undefined | Имя участника: даёт инициалы и доступное имя аватара. |
src | string | undefined | undefined | — |
fallbackSrc | string | undefined | undefined | Резервная картинка: показывается, когда `src` не загрузился. |
alt | string | undefined | undefined | — |
shape | GrAvatarShape | undefined | undefined | — |
status | "online" | "offline" | "busy" | "away" | undefined | undefined | Статус участника. Точка декоративна — рядом идёт скрытая подпись. |
Slots
| Slot | Type | Описание |
|---|---|---|
default | any | Содержимое вместо картинки и инициалов. |
Примеры 3
Размеры, круг и квадрат
Минимальный набор размеров и форм помогает быстро понять, как GrAvatar ведёт себя для людей и для team/workspace сущностей.
<script setup lang="ts">
import { GrAvatar } from '@feugene/granularity'
</script>
<template>
<div class="grid gap-4">
<div class="flex flex-wrap items-center gap-4">
<GrAvatar :size="32">AD</GrAvatar>
<GrAvatar :size="40">AD</GrAvatar>
<GrAvatar :size="56">AD</GrAvatar>
<GrAvatar :size="72">AD</GrAvatar>
</div>
<div class="flex flex-wrap items-center gap-4">
<GrAvatar :size="40" shape="square">QA</GrAvatar>
<GrAvatar :size="56" shape="square">PM</GrAvatar>
<GrAvatar :size="72" shape="square">UX</GrAvatar>
</div>
</div>
</template>Картинка и откат в слот по умолчанию
Показываем основной contract компонента: src рендерит изображение, а при его отсутствии тот же размер сохраняется для fallback-контента.
<script setup lang="ts">
import { GrAvatar, GrCard } from '@feugene/granularity'
const avatarSvg = encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" fill="none">
<rect width="96" height="96" fill="#dbeafe" />
<circle cx="48" cy="36" r="16" fill="#2563eb" opacity="0.18" />
<path d="M18 80c6-15 18-23 30-23s24 8 30 23" fill="#2563eb" opacity="0.26" />
<circle cx="48" cy="36" r="13" fill="#2563eb" />
</svg>
`)
const avatarImageSrc = `data:image/svg+xml;charset=UTF-8,${avatarSvg}`
</script>
<template>
<div class="grid gap-4 lg:grid-cols-[minmax(0,1fr)_220px] lg:items-center">
<div class="flex flex-wrap items-center gap-4">
<GrAvatar :size="40" :src="avatarImageSrc" alt="Alex Doe" />
<GrAvatar :size="56" :src="avatarImageSrc" alt="Alex Doe" status="online" />
<GrAvatar :size="72" shape="square" :src="avatarImageSrc" alt="Alex Doe" />
<!-- Битая ссылка — основной сценарий отказа: показываются инициалы из `name`. -->
<GrAvatar :size="56" src="/broken-avatar.png" name="Alex Doe" />
<GrAvatar :size="56" src="/broken-avatar.png" :fallback-src="avatarImageSrc" name="Alex Doe" />
</div>
<GrCard class="grid gap-2 p-4 text-sm text-[var(--gr-muted-fg)]">
<div class="font-semibold text-[var(--gr-fg)]">
Fallback contract
</div>
<div>
A broken `src` falls back to `fallbackSrc`, then to initials from `name` — the browser never shows its
broken-image icon. Without `src` the default slot is rendered as before.
</div>
</GrCard>
</div>
</template>Композиция в строке участника или команды
На практике GrAvatar почти всегда живёт рядом с именем, ролью и secondary text — поэтому документируем и такой composed layout.
<script setup lang="ts">
import { GrAvatar, GrAvatarGroup, GrCard } from '@feugene/granularity'
const team = [
{ name: 'Alex Doe', status: 'online' as const },
{ name: 'Quinn Ali', status: 'busy' as const },
{ name: 'Sam Rivera', status: 'away' as const },
{ name: 'Noor Haddad', status: 'offline' as const },
]
</script>
<template>
<div class="grid gap-3">
<GrCard class="grid gap-3 p-4">
<div class="flex items-center gap-3">
<GrAvatar :size="44" name="Alex Doe" status="online" />
<div>
<div class="text-sm font-semibold text-[var(--gr-fg)]">
Alex Doe
</div>
<div class="text-sm text-[var(--gr-muted-fg)]">
Engineering lead
</div>
</div>
</div>
</GrCard>
<GrCard class="grid gap-3 p-4">
<div class="text-sm font-semibold text-[var(--gr-fg)]">
Release squad
</div>
<GrAvatarGroup :max="3" :total="9" size="md" aria-label="Release squad">
<GrAvatar v-for="member in team" :key="member.name" :name="member.name" :status="member.status" />
</GrAvatarGroup>
<div class="text-sm text-[var(--gr-muted-fg)]">
Стекинг с «+N»: группа объявляет диктору и имя, и число скрытых участников.
</div>
</GrCard>
</div>
</template>