GrEmptyState

Пакет: @feugene/granularityядроГруппа: Обратная связь

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

Когда брать

  • список пуст с самого начала — «пока нет выплат»: объяснение и следующий шаг вместо пустого прямоугольника;
  • фильтр ничего не нашёл — текст и действие тут другие: сбросить фильтр, а не создать первую запись;
  • раздел ещё не настроен — иконка, заголовок, описание и кнопка вместо инструкции в поддержке;
  • состояние встроено в другой компонент — списки, таблицы и графики показывают его слотом #empty.

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

НужноБерите
Данные грузятсяGrSkeleton / GrLoading
Запрос упалGrResponseErrorBanner
Предупреждение в потоке страницыGrAlert
Нужен просто текст без иллюстрацииGrCard

Пустой экран без объяснения читается как поломка. Разница между «здесь пока ничего нет» и «по этому фильтру ничего не нашлось» — важна: в первом случае предлагают создать, во втором — снять фильтр.

Заголовок

<GrEmptyState title="Пока нет выплат" description="Создайте первую выплату, чтобы отслеживать согласования.">
  <GrButton size="sm">Создать выплату</GrButton>
</GrEmptyState>

Заголовок — настоящий heading: без него пустое состояние не находится навигацией по заголовкам, а объясняет происходящее на экране именно оно. Уровень задаётся headingLevel (26, по умолчанию 3) и настраивается глобально через componentDefaults.GrEmptyState.headingLevel. h1 в наборе нет намеренно: пустое состояние живёт внутри уже существующей структуры страницы, а не задаёт её.

title необязателен: без него и без слота берётся строка из локали (gr.emptyState.title), так что <GrEmptyState /> не превращается в пустую карточку.

Слоты

СлотЗачем
titleзаголовок разметкой: ссылка, выделение, счётчик
descriptionописание разметкой
iconсвоя иконка вместо встроенной lucide/inbox
по умолчаниюдействия под текстом, центрируются

Слот сильнее одноимённого пропа. Встроенная иконка декоративна (aria-hidden) — смысл несёт текст.

Поверхность

<GrCard>
  <GrEmptyState variant="ghost" title="Никого не пригласили" />
</GrCard>

variant (outlined по умолчанию, ghost) повторяет словарь GrCard: ghost снимает рамку, фон и радиус. Он нужен внутри уже существующей карточки — в GrDataTable #empty, GrList #empty или в своём макете, где outlined даёт вторую рамку вокруг той же поверхности.

Размер

size (xslg, по умолчанию md, читается из GrConfigProvider) двигает падинг карточки, коробку иконки, саму иконку, кегль заголовка и вертикальные отступы. xs и sm — для пустой ячейки таблицы и панели дропдауна, где полноразмерное состояние занимает весь список.

Оформление

Всё берётся из токенов: радиус — --gr-radius-md/--gr-radius-lg по ступени, кегли — --gr-text-xs/--gr-text-sm/--gr-text-base, цвета — --gr-card, --gr-brd, --gr-muted, --gr-muted-fg.

Playground 4

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

Код
<GrEmptyState />

Установка

npm i @feugene/granularity

Импорт

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

API

Props

PropTypeпо умолчаниюОписание
variant"ghost" | "outlined" | undefinedundefined`ghost` снимает рамку и фон: карточка внутри карточки рисует вторую рамку.
titlestring | undefinedundefinedЗаголовок. Не задан — берётся из локали; слот `#title` сильнее обоих.
size"xs" | "sm" | "md" | "lg" | undefinedundefined
descriptionstring | undefinedundefined
headingLevel2 | 3 | 4 | 5 | 6 | undefinedundefined

Slots

SlotTypeОписание
iconanyИконка вместо встроенной.
titleanyЗаголовок разметкой: ссылка, выделение, счётчик.
descriptionany
defaultanyДействия под текстом: центрируются.

Примеры 3

Главное действие внутри карточки

Классический сценарий: иконка, заголовок-heading, описание и основное действие в слоте по умолчанию.

No payouts yet

Create the first scheduled payout to start tracking approval and transfer states.

Action
<script setup lang="ts">
import { GrButton, GrEmptyState } from '@feugene/granularity'
</script>

<template>
  <GrEmptyState title="No payouts yet" description="Create the first scheduled payout to start tracking approval and transfer states.">
    <GrButton size="sm">Create payout</GrButton>
  </GrEmptyState>
</template>

Поиск и фильтр без результатов

Ноль результатов поиска: фильтр сверху, компактный size="sm" и действия по сбросу или созданию объекта.

Nothing found

No saved views match “treasury”. Try a broader term or create a new filtered workspace.

Search
<script setup lang="ts">
import { computed, ref } from 'vue'

import { GrButton, GrEmptyState, GrInput } from '@feugene/granularity'

const query = ref('treasury')

const description = computed(() => {
  return `No saved views match “${query.value}”. Try a broader term or create a new filtered workspace.`
})
</script>

<template>
  <div class="grid gap-3">
    <GrInput v-model="query" placeholder="Search views" />

    <GrEmptyState size="sm" title="Nothing found" :description="description">
      <div class="flex flex-wrap justify-center gap-2">
        <GrButton size="sm" variant="outline">Clear filter</GrButton>
        <GrButton size="sm">Create view</GrButton>
      </div>
    </GrEmptyState>
  </div>
</template>

Внутри двухпанельной раскладки

variant="ghost" внутри уже существующей карточки: без него получалась карточка в карточке со второй рамкой.

Left rail can keep filters, contextual hints or a compact KPI while the main area uses `GrEmptyState`.

No team members invited

Inside an existing card the ghost variant drops the second border around the same surface.

Split Layout
<script setup lang="ts">
import { GrButton, GrCard, GrEmptyState } from '@feugene/granularity'
</script>

<template>
  <GrCard class="grid gap-4 md:grid-cols-[minmax(0,220px)_1fr] md:items-center">
    <div class="rounded-xl border border-dashed border-[var(--gr-brd)] bg-[var(--gr-muted)] p-4 text-sm text-[var(--gr-muted-fg)]">
      Left rail can keep filters, contextual hints or a compact KPI while the main area uses `GrEmptyState`.
    </div>

    <GrEmptyState variant="ghost" title="No team members invited" description="Inside an existing card the ghost variant drops the second border around the same surface.">
      <GrButton size="sm">Invite teammate</GrButton>
    </GrEmptyState>
  </GrCard>
</template>

Документация компонентаВсе компоненты