MainMenu

Legacy MainMenu (Figma navigationOldDrawer / navigationOldDrawerMobile). Кнопка с MainMenuSVG открывает drawer навигации.

  • Desktop — двухколоночный @cloud-ru/ds-drawer (DrawerCustom): левая колонка 256px, divider, правая с поиском и категориями.
  • Mobile — по умолчанию левый одноколоночный drawer (не BottomSheet): заголовок «Навигация», scroll-body со sticky-поиском. Через customMobileMenu можно подставить mobile-меню из @cloud-ru/ds-uikit-product-header (bottom sheet).

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

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

  • Нужно главное меню консоли с селектором платформ, административными пунктами и сеткой сервисов.
  • Состав колонок задаётся слотами и данными (serviceGroups, settingItems, search).

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

  • Только верхняя полоса без drawer:
  • Новая продуктовая навигация без legacy-контрактов:

Анатомия

Trigger и drawer

  • trigger — @cloud-ru/ds-button neutral / simple / m с MainMenuSVG.
  • desktop drawer — @cloud-ru/ds-drawer DrawerCustom, слева, ширина по умолчанию 1120px (data-test-id='header__drawer-menu').
  • mobile drawer — левый full-height drawer (navigationOldDrawerMobile), не BottomSheet; data-test-id='header__drawer-menu-mobile'.

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

  • leftTop — обычно селектор платформ (PlatformSelector).
  • settingItems — административные пункты списка.
  • sidebarBottomSlot — слот внизу колонки (например, баннер новой навигации).

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

  • rightTop — баннеры (referral / marketplace).
  • search — поиск через useSearch или свой объект (NavigationSearch); закреплён структурно вне Scroll.
  • serviceGroups — секции категорий сервисов (ServicesCategory + CardServiceLight).

Mobile body

Порядок в scroll-body:

  1. leftTop — в микрофронте и Figma: два PlatformSelector (платформа + проект с avatarName).
  2. search — свёрнутый NavigationSearch; при скролле остаётся sticky (position: sticky).
  3. rightTop — баннеры в колонку (data-mobile на контейнере).
  4. serviceGroups — те же категории в сетке из одной колонки (data-mobile на .services); favorite на карточках всегда виден.
  5. settingItems — CardServiceLight + Divider внизу scroll-body.

sidebarBottomSlot на mobile не рендерится (legacy-поведение).

customMobileMenu

Только на mobile: заменяет встроенный legacy MenuMobile (левый drawer). Типичный сценарий — микрофронт с legacy HeaderLayout и новым mobile-меню из @cloud-ru/ds-uikit-product-header.

  • MenuMobile импортируется из @cloud-ru/ds-uikit-product-header, не из @cloud-ru/ds-uikit-product-header-legacy.
  • Передаются те же controlled open / setOpen, что и в MainMenu — иначе кнопка-триггер и drawer разойдутся.
  • Данные для MenuMobile маппятся на API нового header (segments, settingItems и т.д.) отдельно от legacy-пропов MainMenu.

open / setOpen

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

Установка

pnpm add @cloud-ru/ds-uikit-product-header-legacy
import { MainMenu, useSearch } from '@cloud-ru/ds-uikit-product-header-legacy'

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

Базовый состав

Базовый составleftTop, баннеры, settingItems и serviceGroups. Drawer открывается по клику на MainMenuSVG.
tsx
import { MainMenu } from '@cloud-ru/ds-uikit-product-header-legacy';
import { useState } from 'react';

import { MarketplaceBanner } from '../../../stories/MainMenu/helperComponents/MarketplaceBanner';
import { NewNavigationBanner } from '../../../stories/MainMenu/helperComponents/NewNavigationBanner';
import { PlatformSelector } from '../../../stories/MainMenu/helperComponents/PlatformSelector';
import { ReferralBanner } from '../../../stories/MainMenu/helperComponents/ReferralBanner';
import {
  ADMINISTRATIVE_SECTION,
  DEFAULT_PLATFORM_OPTION,
  MARKETPLACE_BANNER,
  NEW_NAVIGATION_BANNER,
  PLATFORM_OPTIONS_BY_ID,
  PLATFORM_SELECTOR_ITEMS,
  REFERRAL_BANNER,
  SERVICE_GROUPS,
} from '../../fixtures';

export function Basic() {
  const [open, setOpen] = useState(false);
  const [platformId, setPlatformId] = useState(DEFAULT_PLATFORM_OPTION.id);
  const selected = PLATFORM_OPTIONS_BY_ID[platformId] ?? DEFAULT_PLATFORM_OPTION;

  return (
    <MainMenu
      open={open}
      setOpen={setOpen}
      leftTop={
        <PlatformSelector
          label={selected.label}
          description={selected.description}
          variant={selected.variant}
          items={PLATFORM_SELECTOR_ITEMS}
          value={platformId}
          onChange={id => setPlatformId(String(id))}
        />
      }
      rightTop={
        <>
          <ReferralBanner {...REFERRAL_BANNER} href='#' />
          <MarketplaceBanner {...MARKETPLACE_BANNER} href='#' />
        </>
      }
      settingItems={ADMINISTRATIVE_SECTION}
      serviceGroups={SERVICE_GROUPS}
      sidebarBottomSlot={<NewNavigationBanner {...NEW_NAVIGATION_BANNER} />}
    />
  );
}

С поиском

С поискомsearch из useSearch. Drawer открывается по клику на MainMenuSVG.
tsx
import { MainMenu, useSearch } from '@cloud-ru/ds-uikit-product-header-legacy';
import { useState } from 'react';

import { MarketplaceBanner } from '../../../stories/MainMenu/helperComponents/MarketplaceBanner';
import { NewNavigationBanner } from '../../../stories/MainMenu/helperComponents/NewNavigationBanner';
import { PlatformSelector } from '../../../stories/MainMenu/helperComponents/PlatformSelector';
import { ReferralBanner } from '../../../stories/MainMenu/helperComponents/ReferralBanner';
import {
  ADMINISTRATIVE_SECTION,
  DEFAULT_PLATFORM_OPTION,
  MARKETPLACE_BANNER,
  NEW_NAVIGATION_BANNER,
  PLATFORM_OPTIONS_BY_ID,
  PLATFORM_SELECTOR_ITEMS,
  REFERRAL_BANNER,
  SERVICE_GROUPS,
} from '../../fixtures';

export function WithSearch() {
  const [open, setOpen] = useState(false);
  const [platformId, setPlatformId] = useState(DEFAULT_PLATFORM_OPTION.id);
  const search = useSearch();
  const selected = PLATFORM_OPTIONS_BY_ID[platformId] ?? DEFAULT_PLATFORM_OPTION;

  return (
    <MainMenu
      open={open}
      setOpen={setOpen}
      leftTop={
        <PlatformSelector
          label={selected.label}
          description={selected.description}
          variant={selected.variant}
          items={PLATFORM_SELECTOR_ITEMS}
          value={platformId}
          onChange={id => setPlatformId(String(id))}
        />
      }
      rightTop={
        <>
          <ReferralBanner {...REFERRAL_BANNER} href='#' />
          <MarketplaceBanner {...MARKETPLACE_BANNER} href='#' />
        </>
      }
      search={search}
      settingItems={ADMINISTRATIVE_SECTION}
      serviceGroups={SERVICE_GROUPS}
      sidebarBottomSlot={<NewNavigationBanner {...NEW_NAVIGATION_BANNER} />}
    />
  );
}

Mobile-поверхность

Mobile-поверхностьAdaptiveProvider layoutType=mobile: два селектора в leftTop (платформа + проект с avatarName), sticky-поиск, баннеры в колонку. sidebarBottomSlot не передаётся — на mobile не рендерится.
tsx
import { AdaptiveProvider, LAYOUT_TYPE } from '@cloud-ru/ds-adaptive';
import { MainMenu, useSearch } from '@cloud-ru/ds-uikit-product-header-legacy';
import { useState } from 'react';

import { MarketplaceBanner } from '../../../stories/MainMenu/helperComponents/MarketplaceBanner';
import { PlatformSelector } from '../../../stories/MainMenu/helperComponents/PlatformSelector';
import { ReferralBanner } from '../../../stories/MainMenu/helperComponents/ReferralBanner';
import {
  ADMINISTRATIVE_SECTION,
  DEFAULT_PLATFORM_OPTION,
  DEFAULT_PROJECT_OPTION,
  MARKETPLACE_BANNER,
  PLATFORM_OPTIONS_BY_ID,
  PLATFORM_SELECTOR_ITEMS,
  PROJECT_DESCRIPTION,
  PROJECT_OPTIONS_BY_ID,
  PROJECT_SELECTOR_ITEMS,
  REFERRAL_BANNER,
  SERVICE_GROUPS,
} from '../../fixtures';

/**
 * Mobile-поверхность MainMenu: `leftTop` — PlatformSelector (платформа) + PlatformSelector (проект с `avatarName`).
 * `sidebarBottomSlot` на mobile не рендерится.
 */
export function MobileSurface() {
  const [open, setOpen] = useState(false);
  const [platformId, setPlatformId] = useState(DEFAULT_PLATFORM_OPTION.id);
  const [projectId, setProjectId] = useState(DEFAULT_PROJECT_OPTION.id);
  const search = useSearch();

  const selectedPlatform = PLATFORM_OPTIONS_BY_ID[platformId] ?? DEFAULT_PLATFORM_OPTION;
  const selectedProject = PROJECT_OPTIONS_BY_ID[projectId] ?? DEFAULT_PROJECT_OPTION;

  return (
    <AdaptiveProvider layoutType={LAYOUT_TYPE.Mobile}>
      <MainMenu
        open={open}
        setOpen={setOpen}
        leftTop={
          <>
            <PlatformSelector
              label={selectedPlatform.label}
              description={selectedPlatform.description}
              variant={selectedPlatform.variant}
              items={PLATFORM_SELECTOR_ITEMS}
              value={platformId}
              onChange={id => setPlatformId(String(id))}
            />
            <PlatformSelector
              label={selectedProject.label}
              description={PROJECT_DESCRIPTION}
              avatarName={selectedProject.label}
              items={PROJECT_SELECTOR_ITEMS}
              value={projectId}
              onChange={id => setProjectId(String(id))}
            />
          </>
        }
        rightTop={
          <>
            <ReferralBanner {...REFERRAL_BANNER} href='#' />
            <MarketplaceBanner {...MARKETPLACE_BANNER} href='#' />
          </>
        }
        search={search}
        settingItems={ADMINISTRATIVE_SECTION}
        serviceGroups={SERVICE_GROUPS}
      />
    </AdaptiveProvider>
  );
}

Новое mobile-меню через customMobileMenu

Controlled-состояние drawer общее для trigger и mobile-меню:

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

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

  return (
    <MainMenu
      open={open}
      setOpen={setOpen}
      serviceGroups={legacyServiceGroups}
      /* …остальные legacy-пропы для desktop… */
      customMobileMenu={
        <MenuMobile
          open={open}
          setOpen={setOpen}
          segments={headerSegments}
          /* …пропы API @cloud-ru/ds-uikit-product-header… */
        />
      }
    />
  );
}

Props

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

Desktop-first: раскладку берёт AdaptiveProvider в корне приложения / Storybook. Переключение toolbar layoutType=mobile показывает mobile-поверхность без смены API.

ПропDesktopMobile
customMobileMenu—Заменяет legacy MenuMobile; без пропа — левый drawer
sidebarBottomSlotНижний слот левой колонкиНе рендерится
settingItemsКарточки в левой колонкеCardServiceLight + Divider внизу scroll-body
leftTopОдин селектор платформыПлатформа + проект (два PlatformSelector)
searchВне Scroll, всегда развёрнутSticky в scroll-body, сначала свёрнут
rightTop / serviceGroupsПравая колонкаОдноколоночный scroll-body; сетка сервисов — 1 колонка

Форс конкретной раскладки в Storybook — toolbar-global layoutType или вложенный AdaptiveProvider / withLayoutType.

Целевая композиция mobile (как в Figma) — story Examples / WithSampleContent.

Storybook

WithSampleContent (Figma mobile composition):

Figma

Desktop (navigationOldDrawer):

Mobile (navigationOldDrawerMobile):

Смотри также