GrNavbar

  • ядро
  • Навигация
  • API · 6 props

Берут, когда у приложения есть верхняя панель.

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

Обзор

Когда брать

  • у приложения есть верхняя панель — заголовок, поиск, профиль, уведомления в трёх зонах;
  • панель должна оставаться видимой — sticky при прокрутке длинных страниц;
  • на мобильном нужна кнопка меню — showMenuButton открывает GrDrawer или GrSidebar;
  • нужен лендмарк banner — корень рендерится тегом <header>, и обход по ориентирам работает.

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

НужноБерите
Боковая навигацияGrSidebar
Нижняя панель разделов на мобильномGrBottomNav
Панель действий над таблицей или дашбордомGrButtonGroup / GrDashboardToolbar
Путь до текущей страницыGrBreadcrumbs

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

Сцена

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

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

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

  • Шапка админки

    Корень — лендмарк <header>, и до шапки можно дойти по структуре. Слот title держит знак продукта, left — разделы сразу за ним, а слот по умолчанию — глобальные действия у правого края: поиск с сочетанием, уведомления со счётчиком, аккаунт.

    3 unread notificationsMO
    InvoicesThe page under the header changes; the header stays the same on every page.
  • Шапка телефона со своим меню приложения

    На телефоне разделы прячутся за кнопкой меню: show-menu-button рисует её с локализованным именем и шлёт menu. Открывается собственный ящик приложения — шапка со своим ящиком спорила бы с любой оболочкой, у которой он уже есть.

    Orders
    MO
    #5523 · Harbour Road, cabinet B2
    #5524 · Grafton St, boiler service
    #5527 · Dock Mill, alarm test
  • Липкая шапка над документом

    sticky держит шапку наверху, пока документ прокручивается под ней; её слой ниже якорных панелей, поэтому открытое меню не прячется. Зона center берёт путь и одна растягивается, а heading-level делает название документа настоящим h1.

    Cleaning agreementSaved 2 min ago

    GW

    1. This agreement sets out how Northwind Traders supplies cleaning services to Contoso Ltd at its Dublin office.

    2. Services are provided on weekdays between 18:00 and 22:00. Additional visits are booked through the client portal at least two working days ahead.

    3. Invoices are issued on the first working day of each month and are payable within thirty days. Late payments accrue interest at 1.5% a month.

    4. Either party may end the agreement with sixty days written notice. Equipment provided by the supplier remains its property.

    5. The agreement is governed by the laws of Ireland. Disputes go to mediation before any court proceedings.

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

Props

PropTypeпо умолчаниюОписание
titlestring | undefinedundefinedЗаголовок строкой. Слот `#title` сильнее и позволяет обойтись без пропа.
size"xs" | "sm" | "md" | "lg" | undefinedundefinedСтупень кнопки меню — и только её: остальное в шапке принадлежит потребителю, а высоту ряда задаёт `--gr-navbar-height`. Фолбэк — `sm` (32px), а не общий для пакета `md`: кнопка была этого размера с самого начала, и подъём по умолчанию сдвинул бы шапку у всех. Нужен тач-таргет — `size="lg"` даёт 44px, ровно то, чего требуют WCAG 2.5.5 и Apple HIG; в 56px ряда он помещается.
headingLevel2 | 1 | 3 | 4 | 5 | 6 | undefinedundefinedСделать заголовок настоящим `h1`…`h6`. По умолчанию заголовок — `div`, и это верно для большинства шапок: логотип и название приложения заголовками не являются, и `h1` на них исказил бы структуру страницы. Но шапка раздела админки или имя документа — заголовок и есть, а обойти такую страницу по структуре иначе нечем. Через `GrConfigProvider` не настраивается намеренно: «заголовка нет» там не выразить (`useGrComponentProp` требует не-nullable фолбэк), то есть общим дефолтом стал бы какой-то уровень. Да и шапка у оболочки одна — глобальный дефолт настраивал бы единственное место вызова.
showMenuButtonboolean | undefinedfalseКнопка-«бургер» слева от заголовка; нажатие приходит событием `menu`. По умолчанию `false`.
menuButtonClassstring | undefined""Extra classes applied to the menu button wrapper (e.g. `sm:hidden`).
stickyboolean | undefinedfalseПанель прилипает к верху при прокрутке. Слой — `--gr-z-navbar`: он ниже якорных панелей, чтобы открытый список не уезжал под шапку.

Слоты

SlotTypeОписание
titleanyЗаголовок целиком — вместо строки `title`.
leftanyЗона сразу после заголовка: вкладки, переключатель раздела.
centeranyЦентральная зона: поиск, хлебные крошки.
defaultanyПравая зона: действия, аватар.

События

EventСигнатураОписание
menu(event: "menu"): void—

Типы

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

Импорт
import type { GrNavbarProps, GrNavbarEmits, GrNavbarHeadingLevel } from '@feugene/granularity/components/GrNavbar'

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

GrNavbarPropsinterface

GrNavbar — верхняя панель приложения (header) с тремя зонами: слева заголовок и кнопка меню, по центру — свободная зона (поиск, хлебные крошки), справа — действия и аватар. - Корень — <header> (landmark), без собственного role. - aria-label кнопки меню локализуется через useGranularityTranslations.

interface GrNavbarProps {
    /** Заголовок строкой. Слот `#title` сильнее и позволяет обойтись без пропа. */
    title?: string;
    /**
     * Сделать заголовок настоящим `h1`…`h6`.
     *
     * По умолчанию заголовок — `div`, и это верно для большинства шапок: логотип
     * и название приложения заголовками не являются, и `h1` на них исказил бы
     * структуру страницы. Но шапка раздела админки или имя документа — заголовок
     * и есть, а обойти такую страницу по структуре иначе нечем.
     *
     * Через `GrConfigProvider` не настраивается намеренно: «заголовка нет» там не
     * выразить (`useGrComponentProp` требует не-nullable фолбэк), то есть общим
     * дефолтом стал бы какой-то уровень. Да и шапка у оболочки одна — глобальный
     * дефолт настраивал бы единственное место вызова.
     */
    headingLevel?: GrNavbarHeadingLevel;
    /** Кнопка-«бургер» слева от заголовка; нажатие приходит событием `menu`. По умолчанию `false`. */
    showMenuButton?: boolean;
    /**
     * Ступень кнопки меню — и только её: остальное в шапке принадлежит
     * потребителю, а высоту ряда задаёт `--gr-navbar-height`.
     *
     * Фолбэк — `sm` (32px), а не общий для пакета `md`: кнопка была этого размера
     * с самого начала, и подъём по умолчанию сдвинул бы шапку у всех. Нужен
     * тач-таргет — `size="lg"` даёт 44px, ровно то, чего требуют WCAG 2.5.5 и
     * Apple HIG; в 56px ряда он помещается.
     */
    size?: GrComponentSize;
    /** Extra classes applied to the menu button wrapper (e.g. `sm:hidden`). */
    menuButtonClass?: string;
    /**
     * Панель прилипает к верху при прокрутке. Слой — `--gr-z-navbar`: он ниже
     * якорных панелей, чтобы открытый список не уезжал под шапку.
     */
    sticky?: boolean;
}
GrNavbarEmitsinterface
interface GrNavbarEmits {
    /** Нажата кнопка меню — сигнал открыть боковую навигацию. */
    (e: 'menu'): void;
}

Файлdist/types/components/GrNavbar/grNavbarStyles.d.ts

GrNavbarHeadingLeveltype
type GrNavbarHeadingLevel = typeof GR_NAVBAR_HEADING_LEVELS[number];

Файлdist/types/components/GrNavbar/defaults.d.ts

GrNavbarConfigurablePropsinterface

Пропы GrNavbar, настраиваемые глобально через componentDefaults.

interface GrNavbarConfigurableProps {
    size: GrComponentSize;
}

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

grNavbarConfigconst
const grNavbarConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrNavbar">;

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

grNavbarSafelistconst
const grNavbarSafelist: string[];

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

Заголовок настоящим heading — по запросу
vue
<GrNavbar title="Договоры" :heading-level="1" />

По умолчанию заголовок шапки — div, и это не упущение: логотип и название приложения заголовками не являются, а h1 на бренде исказил бы структуру страницы. Большинство шапок именно такие.

Но шапка раздела админки или имя открытого документа — заголовок и есть, и без headingLevel обойти такую страницу по структуре нечем: после h1 в ней не оказывается ни одного заголовка.

Единица разрешена, в отличие от GrCard, где шкала начинается с двойки. Карточка никогда не бывает заголовком страницы, а шапка раздела — бывает.

Через GrConfigProvider уровень не настраивается намеренно. «Заголовка нет» там не выразить, то есть общим дефолтом стал бы какой-то уровень — и брендовые шапки всех потребителей молча получили бы заголовок. Да и шапка у оболочки одна: глобальный дефолт настраивал бы единственное место вызова.

Вид от уровня не зависит: кегль и вес те же, а браузерный отступ заголовка погашен явно. Префлайт пакета сбрасывает margin только у body, поэтому приложение с tailwind-совместимым сбросом его и не увидело бы, — но высота шапки не должна зависеть от того, подключил ли потребитель чужой reset.

Три зоны
vue
<GrNavbar>
  <template #title>
    <RouterLink to="/">Granularity</RouterLink>
  </template>

  <template #center>
    <GrInput placeholder="Поиск" size="sm" aria-label="Поиск" />
  </template>

  <GrButton size="sm">Опубликовать</GrButton>
</GrNavbar>
СлотГде
titleслева, после кнопки меню; заменяет строку title
leftсразу за заголовком — вкладки, переключатель раздела
centerпо центру панели — поиск, хлебные крошки
по умолчаниюсправа — действия, аватар

Проп title необязателен: заголовок собирается разметкой через слот. Когда нет ни строки, ни слота, блок заголовка не рендерится и не съедает отступ.

Центральная зона появляется только вместе со слотом. При ней боковые зоны делят остаток поровну — иначе «центр» считался бы от остатка и уезжал вслед за более широким боком.

Когда места не хватает

Действия справа — глобальный интерфейс: поиск, уведомления, аватар. Эта зона не сжимается никогда. Уступают left и center: они не шире остатка и прокручиваются вбок без видимой полосы. Прокрутка, а не обрезка — ссылка за краем остаётся достижимой, фокус с клавиатуры прокручивает зону к ней. Раньше переполненный left рисовался поверх действий: на 600px «Reports» ложился на «Search». Действий шире своей доли при центральной зоне — тоже без сжатия: центр в этом случае смещается, но ничего не перекрывает.

Шапка с большим числом разделов на узком экране — повод убрать их в меню (showMenuButton), а не надеяться на прокрутку.

Прилипание
vue
<GrNavbar sticky />

sticky оставляет панель наверху при прокрутке. Слой — --gr-z-navbar (900): ниже якорных панелей (--gr-z-dropdown — 1000), поэтому открытый список, тултип или модалка панель перекрывают, а не уезжают под неё. Подробности — ../z-index.md.

Высота

Задаётся переменной --gr-navbar-height (по умолчанию 56px) — на самой панели, на предке или в теме:

css
.app-shell { --gr-navbar-height: 48px; }
Кнопка меню

showMenuButton показывает кнопку и эмитит menu; её доступное имя приходит из локали (gr.navbar.openMenu). menuButtonClass нужен, чтобы прятать кнопку на широких экранах (sm:hidden) — панель не решает за приложение, когда меню сворачивается.

size задаёт ступень только этой кнопки — остальное в шапке принадлежит потребителю, а высоту ряда держит --gr-navbar-height. Читается из GrConfigProvider (componentDefaults.GrNavbar.size).

Фолбэк — sm (32px), а не общий для пакета md: кнопка была этого размера с самого начала, и подъём по умолчанию сдвинул бы шапку у всех. Нужен полноценный тач-таргет — size="lg" даёт 44px и помещается в ряд высотой 56px.