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:
- используйте
HeaderLayout.
- используйте
- Новая продуктовая навигация без legacy-контрактов:
- используйте
@cloud-ru/ds-uikit-product-header.
- используйте
Анатомия
Trigger и drawer
- trigger —
@cloud-ru/ds-buttonneutral / simple / m сMainMenuSVG. - desktop drawer —
@cloud-ru/ds-drawerDrawerCustom, слева, ширина по умолчанию 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:
leftTop— в микрофронте и Figma: дваPlatformSelector(платформа + проект сavatarName).search— свёрнутыйNavigationSearch; при скролле остаётся sticky (position: sticky).rightTop— баннеры в колонку (data-mobileна контейнере).serviceGroups— те же категории в сетке из одной колонки (data-mobileна.services); favorite на карточках всегда виден.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'
Примеры использования
Базовый состав
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} />}
/>
);
}С поиском
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-поверхность
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
Types
WithSupportProps| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
customMobileMenu | ReactNode | — | no | Только mobile: заменяет legacy `MenuMobile` (левый drawer). Например, `MenuMobile` из `@cloud-ru/ds-uikit-product-header` (bottom sheet). Передавайте те же `open` / `setOpen`, что и в `MainMenu` (controlled), иначе триггер и кастомное меню разойдутся. |
data-test-id | string | — | no | |
disabled | boolean | — | no | |
favorite | { value: string[]; onChange: (productId: string) => (addingValue: boolean) => void; } | — | no | |
leftTop | ReactNode | — | no | |
onLinkChange | ((value: string) => void) | — | no | |
open | boolean | — | no | |
platformGroups | LinksGroup[] | — | no | Платформенные группы (например «Облачные продукты», «Другие продукты»). Без поиска в сетке карточек **не отображаются**. С поиском: попадают в результаты при совпадении; порядок — **после** `serviceGroups`, **перед** `settingItems`. Обычно `favoritesEnabled: false`. |
rightTop | ReactNode | — | no | |
search | SearchProps | — | no | |
serviceGroups | LinksGroup[] | — | yes | Основные группы облачных сервисов (инфраструктура, сеть, хранилище и т.п.). Всегда видны в сетке карточек при пустом поиске; участвуют в избранном и боковой навигации. При поиске фильтруются вместе с остальными группами и показываются **первыми** в выдаче. |
setOpen | ((open: boolean) => void) | — | no | |
settingItems | LinksGroup | — | no | Административные сервисы (одна группа, например «Административные сервисы»). Без поиска: пункты в нижней части левой колонки (desktop), не в сетке карточек. С поиском: группа попадает в результаты только при совпадении; отображается **последней** (ниже `serviceGroups` и `platformGroups`). Обычно `favoritesEnabled: false`. Mobile: `CardServiceLight` + `Divider` внизу scroll-body. |
sidebarBottomSlot | ReactNode | — | no | Нижний слот левой колонки sidebar (Figma `bottom > items`). |
Types
WithSupportProps
Адаптивность
Desktop-first: раскладку берёт AdaptiveProvider в корне приложения / Storybook. Переключение toolbar layoutType=mobile показывает mobile-поверхность без смены API.
| Проп | Desktop | Mobile |
|---|---|---|
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):
Смотри также
@cloud-ru/ds-uikit-product-header/ MainMenu — новое меню и экспортMenuMobile(bottom sheet) дляcustomMobileMenu.@cloud-ru/ds-uikit-product-card-predefined—CardServiceLightв сетке категорий и вsettingItemsна mobile.