Перейти к содержанию
Mercury
Русский
Дизайн книга
Меню
Русский

UI.6. Навигация

Tabs, Toolbar, Menubar и Tree используют одну основную остановку Tab и навигацию стрелками внутри. Breadcrumbs и NavigationTree сохраняют нативную навигацию по ссылкам и кнопкам.

Tabs, Toolbar, Menubar и Tree используют общую модель клавиатуры: одна основная остановка Tab, стрелки внутри и зеркальное направление для письма справа налево. Breadcrumbs и NavigationTree сохраняют нативную навигацию по ссылкам и кнопкам. Mercury выводит настоящие ссылки и не выполняет маршрутизацию приложения за вас.

Одна остановка Tab, стрелки внутри

  • Одна остановка: Tabs, Toolbar, Menubar и Tree включают в порядок Tab ровно один элемент — тот, на котором фокус был в последний раз. Поэтому Tab позволяет выйти за пределы виджета целиком.
  • Стрелки внутри: стрелки перемещают фокус между элементами виджета; Home и End ведут к началу и концу.
  • Зеркальное направление: в контексте письма справа налево Left и Right меняются местами. Каждый виджет принимает свойство dir; без него Tabs читает вычисленное направление, а Tree, Toolbar и Menubar — окружение UI, заданное через DirectionProvider.
  • Фокус не равен выбору: перемещение фокуса не подтверждает выбор, если компонент явно этого не предусматривает (автоматические Tabs, selectionFollowsFocus у Tree).

Tabs

Tabs — фасад: передайте tabs и выбранное value; при выборе вкладки он вызовет onChange(value).

  • Вкладка — { label, value, disabled?, content?, panelId? }. При наличии content Tabs выводит панель вкладки; при наличии panelId вкладка управляет панелью, которую вы выводите в другом месте.
  • activationMode: automatic (по умолчанию) выбирает вкладку, получившую фокус; manual лишь перемещает фокус, а выбор происходит по Enter, Space или щелчку.
  • orientation: horizontal (по умолчанию) использует Left и Right; vertical — Up и Down.
  • loop: стрелки замыкают переход с последней вкладки на первую; по умолчанию true.
  • keepMounted: сохраняет содержимое невыбранных панелей смонтированным, но скрытым. По умолчанию содержимое невыбранной панели не выводится.
  • variant: underline (по умолчанию) либо pills.

Части Tabs

Для раскладки, которой нет у фасада, соберите TabsRoot › TabsList › TabsTrigger и по одному TabsPanel на каждый trigger, сопоставляя их по value. Корень принимает value либо defaultValue и onValueChange — запрос, который владелец управляемого состояния может отклонить, — а также те же orientation, dir, activationMode, loop и keepMounted, что и фасад. TabsList принимает variant.

Breadcrumbs показывает путь к текущей странице: ориентир nav, по умолчанию с именем «Breadcrumbs», содержит упорядоченный список нативных ссылок. Его дочерние элементы — Breadcrumb.

  • Текущий элемент ровно один, с aria-current="page": последний, если ни один элемент явно не задаёт current.
  • Элемент disabled — удалённый либо недоступный предок: выводится текстом, не ссылкой; рядом можно показать disabledReason.
  • maxItems сворачивает середину: остаются первый элемент, кнопка скрытых пунктов и последние maxItems - 2 элемента; минимум — 3. Кнопка открывает немодальную панель со скрытыми ссылками в исходном порядке.
  • renderLink выводит каждого предка через ссылку вашего маршрутизатора, которая обязана создавать <a href>.
  • Также: separator (по умолчанию шеврон, зеркальный при письме справа налево), truncate для длинных имён, а также overflowLabel и overflowPanelLabel для имён частей раскрытия.

Mercury не выполняет переход, не отменяет щелчок и не меняет историю: ссылки остаются ссылками.

Tree

Tree — управляемая иерархия сущностей, дерево WAI-ARIA. Четыре состояния разделены: фокус (активный узел), выбор, раскрытие и загрузка.

  • Узлы: каждый TreeNodeData содержит id, label, children, disabled и kind: leaf, branch либо lazy для ещё не загруженных владельцем данных потомков. Ошибка загрузки не превращает ленивый узел в лист.
  • Раскрытие: expandedIds управляется извне; onExpandedChange(ids, change) запрашивает изменение. Ленивый узел сначала вызывает loadChildren(id); при отклонении загрузки узел остаётся раскрываемым с возможностью повторной попытки.
  • Выбор: selectionMode по умолчанию равен none, также доступны single и multiple, с selectedIds и onSelectionChange. selectionFollowsFocus связывает клавиатурный фокус с выбором в одиночном режиме.
  • Клавиши: Up, Down, Home и End перемещают фокус; Right раскрывает узел, затем переходит к первому потомку; Left сворачивает, затем переходит к родителю; Space выбирает; Enter вызывает onActivate(id), который открывает узел и отделён как от раскрытия, так и от выбора.
  • Фокус: activeId, defaultActiveId и onActiveChange. Если владелец удаляет узел с фокусом, фокус переходит к сохранившемуся родителю, иначе — к соседу.
  • Для каждого узла: renderLabel и renderActions. TreeItemAction (переименование, удаление) попадает в порядок Tab лишь пока его узел активен; стрелки и Enter на узле не нажимают это действие.

NavigationTree отличается от Tree: его узлы — маршруты, а значение — текущее местоположение читателя, а не выбор.

  • Каждый узел — { id, label, href?, target?, rel?, download?, children? }. Узел с href — настоящая ссылка; без него — заголовок для потомков.
  • У каждой ветви есть отдельная кнопка раскрытия с именем из «Toggle» и подписи узла, с aria-expanded. Кнопка только раскрывает; ссылка только выполняет переход.
  • currentId отмечает текущий маршрут через aria-current="page". По умолчанию предки текущего маршрута изначально раскрыты, поэтому маршрут виден при открытии страницы; expandedIds либо defaultExpandedIds и onExpandedChange позволяют управлять этим.
  • label обязателен и задаёт имя ориентира nav.
  • onNavigate(href, event) позволяет клиентскому маршрутизатору обработать обычный щелчок основной кнопкой с переходом в той же вкладке. Приложение само выполняет маршрутизацию и вызывает event.preventDefault(). Щелчки с модификаторами, другие вкладки и скачивания сохраняют нативное поведение.

Menubar — строка меню в стиле настольного приложения. Фасад принимает menus, каждый элемент — { label, icon?, items }, а также accent и dir. У пункта есть type: item (по умолчанию), check, radio, label либо separator. Строки принимают label, shortcut, icon, onSelect и disabled; строке check нужен id; строки radio объединяются по group, каждая имеет value; checked задаёт начальное состояние.

В исходнике описано управление клавишами: Left и Right перемещают между кнопками меню, а внутри открытого меню открывают соседнее. Enter, Space и ArrowDown открывают меню на первой строке, ArrowUp — на последней. Escape, выбор строки и Tab закрывают меню с возвратом фокуса на его кнопку.

Составная форма — MenubarRoot с одним MenubarMenuRoot на меню, внутри которого находятся MenubarTrigger и MenubarContent. Содержимое принимает те же части, что и Dropdown: MenubarItem, MenubarCheckboxItem, MenubarRadioGroup, MenubarRadioItem, MenubarItemIndicator, MenubarGroup, MenubarLabel, MenubarSeparator, а также MenubarSub с MenubarSubTrigger и MenubarSubContent.

Toolbar

Toolbar — одна точка входа с клавиатуры в строку или столбец элементов управления. Tab входит в панель и выходит из неё; стрелки перемещают между элементами, Home и End — к началу и концу. Вложенные input, textarea или select сохраняют свои клавиши редактирования. Toolbar внутри другого Toolbar выводится как group, фокусом управляет внешняя панель.

  • Свойства: orientation (horizontal либо vertical), dir, loop (по умолчанию true), size (sm либо md), variant (surface рисует панель, plain только располагает элементы).
  • Недоступные элементы: при focusableWhenDisabled отключённая часть сохраняет возможность фокуса и получает aria-disabled, чтобы объясняющая причину подсказка оставалась доступной. Собственная настройка части имеет приоритет.
  • Части: ToolbarButton (по умолчанию type="button"), ToolbarLink, ToolbarGroup, ToolbarSeparator, а также ToolbarToggleGroup с элементами ToolbarToggleItem. Группа с type="single" задаёт единственный выбор — radiogroup со значением string; с type="multiple" — группу независимых нажатых кнопок со значением string[].

Компонент Mercury Toggle тоже входит в набор Toolbar: он читает контекст панели.

Заголовок страницы

import { useState } from "@fiberfx/hyperscript/hooks";
import { Breadcrumb, Breadcrumbs, Tabs } from "@fiberfx/ui";

type View = "lesson" | "companion";

export function LessonHeader() {
  const [view, setView] = useState<View>("lesson");
  return (
    <header>
      <Breadcrumbs>
        <Breadcrumb href="/">Mercury</Breadcrumb>
        <Breadcrumb href="/ui">UI</Breadcrumb>
        <Breadcrumb href="/ui/navigation">Navigation</Breadcrumb>
      </Breadcrumbs>
      <Tabs<View>
        value={view}
        onChange={setView}
        tabs={[
          { label: "Lesson", value: "lesson", content: <p>The page.</p> },
          { label: "For agents", value: "companion", content: <p>The companion.</p> },
        ]}
      />
    </header>
  );
}

Последняя хлебная крошка становится текущей страницей без явного указания. Вкладки выводят собственные панели; смонтировано содержимое только выбранной.

Остальные компоненты семейства

Компонент Назначение
Accordion последовательность раскрываемых секций
NavigationMenu навигация сайта с панелями ссылок
Pagination пошаговое переключение страниц
TabNav навигационные вкладки в виде ссылок
TreeDestinationPicker сценарий «Переместить в…» по дереву