MainMenu

MainMenu — drawer с каталогом сервисов: сегменты, fuzzy-поиск, избранное, DnD групп и карточек.

  • Desktop — resizable @cloud-ru/ds-drawer слева: левая колонка (избранное, настройки), правая — сегменты и сетка карточек.
  • Mobile — fullscreen bottom sheet (MobileDrawerCustom, position='bottom'): sticky header с логотипом, scroll-body, поиск и каталог.

Поверхность выбирается через AdaptiveProvider (layoutType).

Когда использовать

  • Навигация по каталогу облачных сервисов из шапки консоли (новая продуктовая навигация).
  • Сценарии с избранным, недавними сервисами и настройками отображения карточек.
  • Mobile-меню для микрофронта на legacy HeaderLayout: MenuMobile подключается через customMobileMenu у @cloud-ru/ds-uikit-product-header-legacy / MainMenu.

Когда не нужен:

Анатомия

Trigger и drawer

  • trigger — HeaderButton с MainMenuSVG и tooltip «Сервисы».
  • desktop drawer — resizable DrawerCustom, слева; drag-handle для ширины.
  • mobile drawer — fullscreen bottom sheet; swipe отключён (swipeEnabled={false}).

Левая колонка (desktop)

  • leftTop — слот над избранным (например, селектор платформы).
  • Favorites — избранные сервисы с DnD (MainMenuDndContext).
  • settingItems — административные пункты (mapInnerLinksToListItems).
  • leftBottom — дополнительный слот внизу колонки.

Правая колонка (desktop)

  • rightTop — баннеры.
  • Search — fuzzy-поиск по каталогу.
  • segments — сегменты с SegmentControl (если сегментов > 1).
  • Сетка карточек CardServiceSmall по группам в активном сегменте.

Mobile body

  • MenuHeaderBrand + divider — sticky header с кнопкой закрытия.
  • leftTop, Search, rightTop, Content — в scroll-body.
  • settingItems / leftBottom — нижняя зона (MenuBottom).
  • Избранное без drag&drop (desktop-only DnD).

open / setOpen

Controlled-открытие drawer. Без open состояние держит сам компонент.

MenuMobile экспортируется из @cloud-ru/ds-uikit-product-header отдельно от MainMenu — тот же API (MainMenuProps), но без кнопки-триггера. Используется:

  • внутри MainMenu на mobile-поверхности;
  • в legacy-интеграции: customMobileMenu у @cloud-ru/ds-uikit-product-header-legacy / MainMenu.
import { MainMenu, MenuMobile } from '@cloud-ru/ds-uikit-product-header';

Установка

pnpm add @cloud-ru/ds-uikit-product-header
import { MainMenu, MenuMobile } from '@cloud-ru/ds-uikit-product-header';
import '@cloud-ru/ds-uikit-product-header/style.css';

Примеры использования

Базовое меню

Базовое менюОдин сегмент с группами сервисов. Открывается кнопкой «Меню».
tsx
import { ViewTileSVG } from '@cloud-ru/ds-icons/interface/product';
import { MainMenu } from '@cloud-ru/ds-uikit-product-header';

import { SERVICE_GROUPS } from '../../../stories/demoData';

export function Basic() {
  return (
    <MainMenu
      segments={[
        {
          id: 'allServices',
          label: 'Все сервисы',
          icon: <ViewTileSVG size={24} />,
          items: SERVICE_GROUPS.slice(0, 2),
        },
      ]}
      data-test-id='header-main-menu-basic'
    />
  );
}

С поиском

С поискомControlled `search` фильтрует карточки по fuzzy-совпадению.
tsx
import { ViewTileSVG } from '@cloud-ru/ds-icons/interface/product';
import { MainMenu } from '@cloud-ru/ds-uikit-product-header';
import { useState } from 'react';

import { SERVICE_GROUPS } from '../../../stories/demoData';

export function WithSearch() {
  const [searchValue, setSearchValue] = useState('');

  return (
    <MainMenu
      segments={[
        {
          id: 'allServices',
          label: 'Все сервисы',
          icon: <ViewTileSVG size={24} />,
          items: SERVICE_GROUPS.slice(0, 3),
        },
      ]}
      search={{ value: searchValue, onChange: setSearchValue }}
      data-test-id='header-main-menu-search'
    />
  );
}

С избранным

С избранным`favorite.value` и `favorite.onChange` управляют списком избранных сервисов.
tsx
import { ViewTileSVG } from '@cloud-ru/ds-icons/interface/product';
import { MainMenu } from '@cloud-ru/ds-uikit-product-header';
import { useState } from 'react';

import { SERVICE_GROUPS } from '../../../stories/demoData';

export function WithFavorites() {
  const [favoriteIds, setFavoriteIds] = useState<string[]>(['inference']);

  return (
    <MainMenu
      segments={[
        {
          id: 'allServices',
          label: 'Все сервисы',
          icon: <ViewTileSVG size={24} />,
          items: SERVICE_GROUPS.slice(0, 2),
        },
      ]}
      favorite={{
        value: favoriteIds,
        onChange: productId => (addingValue: boolean) => {
          setFavoriteIds(prev => (addingValue ? [...prev, productId] : prev.filter(id => id !== productId)));
        },
      }}
      data-test-id='header-main-menu-favorites'
    />
  );
}

Интеграция с legacy MainMenu

import { useState } from 'react';
import { MainMenu as LegacyMainMenu } from '@cloud-ru/ds-uikit-product-header-legacy';
import { MenuMobile } from '@cloud-ru/ds-uikit-product-header';

function LocalMainMenu() {
  const [open, setOpen] = useState(false);

  return (
    <LegacyMainMenu
      open={open}
      setOpen={setOpen}
      serviceGroups={legacyGroups}
      customMobileMenu={
        <MenuMobile
          open={open}
          setOpen={setOpen}
          segments={headerSegments}
        />
      }
    />
  );
}

Props

Адаптивность

Проп / поведениеDesktopMobile
DrawerResizable слеваFullscreen bottom sheet
favorite DnDДаНет
segments / SegmentControlДаДа (скрывается при поиске)
preferencesМодалка настроек в тулбареТо же

Storybook

Figma

Смотри также