GrDescriptionList

  • ядро
  • Данные
  • API · 9 props

Берут, когда карточка объекта.

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

Обзор

Когда брать

  • карточка объекта — сводка заказа, профиля, документа: подписи слева, значения колонкой справа;
  • технические реквизиты — идентификаторы, хеши, MIME, версии: длинное значение переносится, а не выталкивает вёрстку;
  • часть значений — не текст — бейдж статуса, относительное время, ссылка подставляются слотом на конкретную пару;
  • пар много — до четырёх колонок складывают длинный список, не разрывая пары;
  • метаданные подписью к чему-то другому — «Сообщений: 3 · Создана: 12.04» внутри пункта списка: flow пускает пары строкой, а не столбцом.

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

НужноБерите
Однородные строки «заголовок и подзаголовок»GrList
Один показатель крупноGrStatistic
Набор объектов по колонкам, с сортировкойGrDataTable
Секция формы с заголовкомGrFormSection
Значения редактируютсяGrFormField

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

Сцена

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

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

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

  • Факты одного объекта

    Строки описывают один заказ, поэтому подписи стоят колонкой шириной label-width, а значения выровнены. name даёт паре свой слот #value-… — бейдж, ссылку, сумму, — а tone красит значение и никогда подпись.

    Order A-20419

    3 items · ships from Rotterdam

    Status
    Unpaid
    Placed
    14 Sep, 09:42
    Payment
    Card declined
    Total
    €1,284.50
  • Метаданные строкой

    layout="flow" пускает короткие пары строкой под заголовком и переносит, где нужно, — это по-прежнему настоящий <dl> с dt и dd, а не текст с двоеточиями. size="sm" берёт ступень ниже, а тон помечает пару, которой нужно внимание.

  • Пусто — не значит нет

    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-2Healthy

    Configuration

    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

PropTypeпо умолчаниюОписание
size"xs" | "sm" | "md" | "lg" | undefinedundefinedКегль подписей и значений по шкале контролов. По умолчанию `md`.
dividedboolean | undefinedundefinedОтбивать пары линиями. В `flow` не применяется.
emptyTextstring | undefinedundefinedЧем печатать пустое значение.
columnsGrDescriptionColumns | undefinedundefinedКолонки сетки. В `flow` не применяются: строка колонок не имеет.
layoutGrDescriptionLayout | undefinedundefined`inline` — подпись слева колонкой; `stacked` — над значением; `flow` — пары текут по строке с переносом (метаданные внутри пункта списка).
labelWidthstring | undefinedundefinedШирина колонки подписей при `inline`.
stackBelownumber | undefinedundefinedНиже какой ширины контейнера (px) `inline` переключается на `stacked`. В узкой колонке фиксированная подпись выжимает значение в букву на строку.
densityGrDescriptionDensity | undefinedundefinedШаг между парами: `compact` сжимает его для плотных карточек. По умолчанию `regular`.
itemsобязательныйreadonly GrDescriptionItem[]—Пары в порядке вывода. `name` пары открывает её слоты `#label-<name>` и `#value-<name>`.

Слоты

SlotTypeОписание
empty{ item: GrDescriptionItem; }Пустое состояние значения — когда у пункта нечего показать.

Типы

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

Импорт
import type { GrDescriptionLayout, GrDescriptionDensity, GrDescriptionColumns } from '@feugene/granularity/components/GrDescriptionList'

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

grDescriptionListConfigconst
const grDescriptionListConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrDescriptionList">;

Файлdist/types/components/GrDescriptionList/grDescriptionListStyles.d.ts

GrDescriptionLayouttype

inline — подпись колонкой слева; stacked — над значением; flow — пары текут по строке с переносом.

type GrDescriptionLayout = 'inline' | 'stacked' | 'flow';
GrDescriptionDensitytype
type GrDescriptionDensity = 'regular' | 'compact';
GrDescriptionColumnstype

Сколько колонок пар. Длинный список читается в несколько.

type GrDescriptionColumns = 1 | 2 | 3 | 4;

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

GrDescriptionIteminterface
interface GrDescriptionItem {
    label: string;
    /** Значение. `null` и `''` печатаются прочерком — строка не исчезает. */
    value?: string | number | null;
    /** Ключ слотов `#value-<name>` и `#label-<name>`. */
    name?: string;
    /** Тон значения. Подпись остаётся нейтральной всегда. */
    tone?: GrTone;
}
GrDescriptionListPropsinterface

Список пар «характеристика → значение»: настоящий <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

GrDescriptionListConfigurablePropsinterface

Пропы 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

grDescriptionListSafelistconst

Пуст: составных классов у списка нет. Тон, размер и плотность выбираются из мап 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 · Последнее: вчера» внутри пункта списка, — колонка не нужна: нужна строка, которая течёт по ширине и переносится.

vue
<GrDescriptionList :items="meta" layout="flow" />

Корень здесь не сетка, поэтому columns, divided и labelWidth не применяются: в строке нет ни колонок, ни ряда, который можно отбить линией.

Семантика при этом не страдает — это по-прежнему настоящий <dl> с парами dt/dd, а не текст с двоеточиями.

Тон — только у значения

Красная подпись читается как «поле сломано», хотя проблема в величине. Поэтому tone у пары красит <dd> и не трогает <dt>.

Слоты именуются по name

#value-<name> и #label-<name> работают для пар, у которых задан name: в парах регулярно стоят GrBadge, относительное время и ссылки. Пары без name остаются строками — ключ нужен именно затем, чтобы слот адресовался к одной паре, а не ко всем сразу.

Границы

Компонент не редактирует значения, не сортирует пары и не группирует их в секции. Секции — GrCard с title вокруг нескольких списков.