GrDescriptionList
Берут, когда карточка объекта.
Обзор
Когда брать
- карточка объекта — сводка заказа, профиля, документа: подписи слева, значения колонкой справа;
- технические реквизиты — идентификаторы, хеши, MIME, версии: длинное значение переносится, а не выталкивает вёрстку;
- часть значений — не текст — бейдж статуса, относительное время, ссылка подставляются слотом на конкретную пару;
- пар много — до четырёх колонок складывают длинный список, не разрывая пары;
- метаданные подписью к чему-то другому — «Сообщений: 3 · Создана: 12.04»
внутри пункта списка:
flowпускает пары строкой, а не столбцом.
Когда взять другое
| Нужно | Берите |
|---|---|
| Однородные строки «заголовок и подзаголовок» | GrList |
| Один показатель крупно | GrStatistic |
| Набор объектов по колонкам, с сортировкой | GrDataTable |
| Секция формы с заголовком | GrFormSection |
| Значения редактируются | GrFormField |
Playground контролы выводятся из API, а не пишутся руками
Загружается…
0 изменено· состояние — в адресе
<GrDescriptionList
:items="items"
labelWidth="7rem"
divided
/>Примеры 5 у каждого — живое демо и его код
Факты одного объекта
Строки описывают один заказ, поэтому подписи стоят колонкой шириной
label-width, а значения выровнены.nameдаёт паре свой слот#value-…— бейдж, ссылку, сумму, — аtoneкрасит значение и никогда подпись.Order A-20419
3 items · ships from Rotterdam
- Status
- Unpaid
- Customer
- Margaret Hamilton
- Placed
- 14 Sep, 09:42
- Payment
- Card declined
- Total
- €1,284.50
Метаданные строкой
layout="flow"пускает короткие пары строкой под заголовком и переносит, где нужно, — это по-прежнему настоящий<dl>сdtиdd, а не текст с двоеточиями.size="sm"берёт ступень ниже, а тон помечает пару, которой нужно внимание.- Retry webhook deliveries with exponential backoff #4182
- Opened
- 2 h ago
- Reviews
- 2 of 2
- Checks
- Passing
- Changes
- +142 −38
- Move invoice PDFs to the EU bucket #4179
- Opened
- yesterday
- Reviews
- 0 of 1
- Checks
- 2 failing
- Changes
- +18 −4
- Drop the legacy CSV export endpoint #4171
- Opened
- 3 days ago
- Reviews
- Changes requested
- Checks
- Passing
- Changes
- +6 −412
- Retry webhook deliveries with exponential backoff #4182
Пусто — не значит нет
nullи''сохраняют строку, а слот#emptyполучаетitemи отвечает каждой по-своему;0— значение. Длинные идентификаторы переносятся, а не распирают вёрстку,#label-…добавляет подписи подсказку, аdensity="compact"уплотняет строки.- Endpoint ID
we_01J8ZQ4K7M3N9P2R5T6V8X0Y1Z- URL
- https://api.acme.test/hooks/billing/v2/events
- Signing secret
- whsec_••••••••••••3f9a
- Events
- invoice.paid, invoice.payment_failed, customer.subscription.updated
- Failed attempts
- 0
- Last delivery
- No deliveries yet
- Fallback URL
Решает контейнер, а не экран
Все четыре панели получают одни и те же пропы:
:columns="2",label-widthи:stack-below="240".columns— потолок, поэтому в широкую панель встают две колонки, в маленькую карточку одна, а в карточке ужеstack-belowподпись уходит над значением, а не выжимает его в букву на строку.db-prod-eu-2HealthyConfiguration
- Engine
- PostgreSQL 17.2
- Class
- 8 vCPU · 32 GB
- Storage
- 1.2 TB of 2 TB
- Region
- eu-central-1
- Replicas
- 2
- Backups
- Daily, kept 14 days
Connection
- Host
- db-prod-eu-2.internal.acme.test
- Port
- 5432
- TLS
- Required
Access
- Owner
- Platform team
- IP allowlist
- 3 ranges
- IAM auth
- Disabled
Maintenance
- Window
- Sun 02:00–03:00 UTC
- Next
- 12 Oct 2026
- Last patch
- 17.1 → 17.2
API порождается из пакета · разойтись не может
Props
| Prop | Type | по умолчанию | Описание |
|---|---|---|---|
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Кегль подписей и значений по шкале контролов. По умолчанию `md`. |
divided | boolean | undefined | undefined | Отбивать пары линиями. В `flow` не применяется. |
emptyText | string | undefined | undefined | Чем печатать пустое значение. |
columns | GrDescriptionColumns | undefined | undefined | Колонки сетки. В `flow` не применяются: строка колонок не имеет. |
layout | GrDescriptionLayout | undefined | undefined | `inline` — подпись слева колонкой; `stacked` — над значением; `flow` — пары текут по строке с переносом (метаданные внутри пункта списка). |
labelWidth | string | undefined | undefined | Ширина колонки подписей при `inline`. |
stackBelow | number | undefined | undefined | Ниже какой ширины контейнера (px) `inline` переключается на `stacked`. В узкой колонке фиксированная подпись выжимает значение в букву на строку. |
density | GrDescriptionDensity | undefined | undefined | Шаг между парами: `compact` сжимает его для плотных карточек. По умолчанию `regular`. |
itemsобязательный | readonly GrDescriptionItem[] | — | Пары в порядке вывода. `name` пары открывает её слоты `#label-<name>` и `#value-<name>`. |
Слоты
| Slot | Type | Описание |
|---|---|---|
empty | { item: GrDescriptionItem; } | Пустое состояние значения — когда у пункта нечего показать. |
Типы
Все экспортируемые типы подпути, по файлам, где они объявлены. Имена, дженерики и тела читаются из файлов деклараций пакета и разойтись не могут.
import type { GrDescriptionLayout, GrDescriptionDensity, GrDescriptionColumns } from '@feugene/granularity/components/GrDescriptionList'Файлdist/types/components/GrDescriptionList/config.d.ts
const grDescriptionListConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDescriptionList">;Файлdist/types/components/GrDescriptionList/grDescriptionListStyles.d.ts
inline — подпись колонкой слева; stacked — над значением; flow — пары
текут по строке с переносом.
type GrDescriptionLayout = 'inline' | 'stacked' | 'flow';type GrDescriptionDensity = 'regular' | 'compact';Сколько колонок пар. Длинный список читается в несколько.
type GrDescriptionColumns = 1 | 2 | 3 | 4;Файлdist/types/components/GrDescriptionList/GrDescriptionList.vue.d.ts
interface GrDescriptionItem {
label: string;
/** Значение. `null` и `''` печатаются прочерком — строка не исчезает. */
value?: string | number | null;
/** Ключ слотов `#value-<name>` и `#label-<name>`. */
name?: string;
/** Тон значения. Подпись остаётся нейтральной всегда. */
tone?: GrTone;
}Список пар «характеристика → значение»: настоящий <dl> с выравниванием
значений в колонку.
В отличие от GrList, где пункт — это «заголовок с подзаголовком», здесь
два разных по роли элемента: термин и его значение.
interface GrDescriptionListProps {
/** Пары в порядке вывода. `name` пары открывает её слоты `#label-<name>` и `#value-<name>`. */
items: readonly GrDescriptionItem[];
/**
* `inline` — подпись слева колонкой; `stacked` — над значением; `flow` —
* пары текут по строке с переносом (метаданные внутри пункта списка).
*/
layout?: GrDescriptionLayout;
/** Ширина колонки подписей при `inline`. */
labelWidth?: string;
/**
* Ниже какой ширины контейнера (px) `inline` переключается на `stacked`.
* В узкой колонке фиксированная подпись выжимает значение в букву на строку.
*/
stackBelow?: number;
/** Шаг между парами: `compact` сжимает его для плотных карточек. По умолчанию `regular`. */
density?: GrDescriptionDensity;
/** Отбивать пары линиями. В `flow` не применяется. */
divided?: boolean;
/** Колонки сетки. В `flow` не применяются: строка колонок не имеет. */
columns?: GrDescriptionColumns;
/** Чем печатать пустое значение. */
emptyText?: string;
/** Кегль подписей и значений по шкале контролов. По умолчанию `md`. */
size?: GrComponentSize;
}Файлdist/types/components/GrDescriptionList/defaults.d.ts
Пропы GrDescriptionList, настраиваемые глобально через
<GrConfigProvider :component-defaults="{ GrDescriptionList: { … } }">.
Только оформление — данные принадлежат экземпляру.
interface GrDescriptionListConfigurableProps {
size: GrComponentSize;
layout: GrDescriptionLayout;
labelWidth: string;
density: GrDescriptionDensity;
divided: boolean;
columns: GrDescriptionColumns;
emptyText: string;
}Файлdist/types/components/GrDescriptionList/safelist.d.ts
Пуст: составных классов у списка нет. Тон, размер и плотность выбираются из
мап grDescriptionListStyles.ts целыми литералами, колонки задаются
инлайновым стилем, и granum извлекает всё это из чанков компонента сам,
включая общие.
const grDescriptionListSafelist: string[];Использование из прозы библиотеки
dl > div > dt + dd, а не <div> с виду похожий
Пара оборачивается в <div>: такая вложенность валидна по HTML5 и нужна для
раскладки. Это и есть причина существования компонента — ручная разметка
дважды дала <dl> с голыми <div> внутри. Выглядело списком, но парой
терминов не было: ни для парсера, ни для скринридера там не было ни
характеристики, ни значения.
Пустое значение печатается, а не пропускается
null, undefined и '' дают прочерк (emptyText, слот #empty), но строка
остаётся. «Поле есть, значения нет» и «поля нет» — разные утверждения;
выпавшая строка ломает и выравнивание, и чтение.
Ноль пустотой не считается: 0 — это значение.
Раскладка сдаётся по ширине контейнера
inline держит подписи колонкой шириной labelWidth, иначе значения
сдвигаются на каждой строке по длине подписи — ровно то, ради чего колонка и
нужна.
stackBelow (в пикселях) переключает раскладку на stacked, когда контейнер
уже порога: в узкой колонке фиксированная подпись выжимает значение в букву на
строку. Меряется контейнер, а не вьюпорт — пары живут и в узкой карточке на
широком экране. До монтирования действует заданная раскладка, поэтому серверный
рендер стабилен.
Порог применяется только к inline: колонка подписей — единственное, что в
узком контейнере ломается. flow переносится сам, и подменять его нечем.
columns раскладывает пары сеткой, а не column-count: пара — элемент сетки и
между колонками не рвётся.
columns — это потолок, а не приказ
Число колонок выбирает CSS по ширине контейнера: columns: 4 значит «до
четырёх». Хватает места на четыре — будет четыре, хватает на две — будет две, а
в колонке 290px останется одна.
Медиазапросы тут не годятся принципиально: они меряют экран, а список живёт в карточке. На широком мониторе в узкой колонке включались бы две колонки, подпись фиксированной ширины съедала бы почти всё место, и значение переносилось бы посимвольно — «30» печаталось как «3» и «0» на двух строках. Показанное число становится неверным, и это уже не вопрос вкуса.
Порог, ниже которого колонка не делится, — --gr-description-list-column-min
(по умолчанию 12rem). Считается всё чистым CSS: ни замеров, ни JS, ни
расхождения между сервером и клиентом.
При inline порог выше: подпись и значение стоят рядом, поэтому колонка
обязана вместить обоих — labelWidth плюс --gr-description-list-value-min
(5rem). Без этой добавки колонка шириной с одну подпись оставляет значению
несколько пикселей, и уже оно переносится посимвольно: тот же дефект, только в
других числах. В stacked и flow ширина подписи в расчёт не идёт — значение
там под ней, а не рядом.
flow — метаданные строкой, а не столбцом
inline и stacked оба ставят пары друг под другом. Когда пары короткие и
служат подписью к чему-то другому — «Сообщений: 3 · Создана: 12.04 · Последнее:
вчера» внутри пункта списка, — колонка не нужна: нужна строка, которая течёт по
ширине и переносится.
<GrDescriptionList :items="meta" layout="flow" />Корень здесь не сетка, поэтому columns, divided и labelWidth не
применяются: в строке нет ни колонок, ни ряда, который можно отбить линией.
Семантика при этом не страдает — это по-прежнему настоящий <dl> с парами
dt/dd, а не текст с двоеточиями.
Тон — только у значения
Красная подпись читается как «поле сломано», хотя проблема в величине. Поэтому
tone у пары красит <dd> и не трогает <dt>.
Слоты именуются по name
#value-<name> и #label-<name> работают для пар, у которых задан name:
в парах регулярно стоят GrBadge, относительное время и ссылки. Пары без
name остаются строками — ключ нужен именно затем, чтобы слот адресовался к
одной паре, а не ко всем сразу.
Границы
Компонент не редактирует значения, не сортирует пары и не группирует их в
секции. Секции — GrCard с title вокруг нескольких списков.
Пропы этого примера