@cloud-ru/ds-uikit-product-header 1.1.1 → 1.1.2-preview-d405de03.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +70 -4
  2. package/dist/cjs/components/MainMenu/helperComponents/Content/Content.js +7 -8
  3. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/styles.module.css +1 -0
  4. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/SortableGroup.d.ts +3 -3
  5. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/SortableGroup.js +2 -2
  6. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SortableGroupHeader.d.ts +3 -3
  7. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SortableGroupHeader.js +8 -2
  8. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/styles.module.css +1 -0
  9. package/dist/cjs/components/MainMenu/helperComponents/MenuDesktop/MenuDesktop.d.ts +1 -1
  10. package/dist/cjs/components/MainMenu/helperComponents/MenuDesktop/MenuDesktop.js +10 -7
  11. package/dist/cjs/components/MainMenu/helperComponents/MenuDesktop/styles.module.css +13 -3
  12. package/dist/cjs/components/MainMenu/types.d.ts +14 -9
  13. package/dist/cjs/components/MainMenu/utils/getLinkEmblem.d.ts +1 -1
  14. package/dist/cjs/components/MainMenu/utils/getLinkEmblem.js +1 -9
  15. package/dist/cjs/components/MainMenu/utils/index.d.ts +0 -1
  16. package/dist/cjs/components/MainMenu/utils/index.js +0 -1
  17. package/dist/cjs/components/PathBreadcrumbs/styles.module.css +3 -0
  18. package/dist/cjs/constants.d.ts +2 -0
  19. package/dist/cjs/constants.js +2 -0
  20. package/dist/cjs/style.css +18 -3
  21. package/dist/esm/components/MainMenu/helperComponents/Content/Content.js +7 -8
  22. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/styles.module.css +1 -0
  23. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/SortableGroup.d.ts +3 -3
  24. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/SortableGroup.js +2 -2
  25. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SortableGroupHeader.d.ts +3 -3
  26. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SortableGroupHeader.js +8 -2
  27. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/styles.module.css +1 -0
  28. package/dist/esm/components/MainMenu/helperComponents/MenuDesktop/MenuDesktop.d.ts +1 -1
  29. package/dist/esm/components/MainMenu/helperComponents/MenuDesktop/MenuDesktop.js +6 -3
  30. package/dist/esm/components/MainMenu/helperComponents/MenuDesktop/styles.module.css +13 -3
  31. package/dist/esm/components/MainMenu/types.d.ts +14 -9
  32. package/dist/esm/components/MainMenu/utils/getLinkEmblem.d.ts +1 -1
  33. package/dist/esm/components/MainMenu/utils/getLinkEmblem.js +1 -9
  34. package/dist/esm/components/MainMenu/utils/index.d.ts +0 -1
  35. package/dist/esm/components/MainMenu/utils/index.js +0 -1
  36. package/dist/esm/components/PathBreadcrumbs/styles.module.css +3 -0
  37. package/dist/esm/constants.d.ts +2 -0
  38. package/dist/esm/constants.js +2 -0
  39. package/dist/esm/style.css +18 -3
  40. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  41. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  42. package/package.json +26 -26
  43. package/src/components/MainMenu/helperComponents/Content/Content.tsx +8 -12
  44. package/src/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/styles.module.scss +1 -0
  45. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/SortableGroup.tsx +11 -8
  46. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SortableGroupHeader.tsx +28 -4
  47. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/styles.module.scss +14 -7
  48. package/src/components/MainMenu/helperComponents/MenuDesktop/MenuDesktop.tsx +26 -4
  49. package/src/components/MainMenu/helperComponents/MenuDesktop/styles.module.scss +13 -3
  50. package/src/components/MainMenu/types.ts +15 -9
  51. package/src/components/MainMenu/utils/getLinkEmblem.tsx +3 -18
  52. package/src/components/MainMenu/utils/index.ts +0 -1
  53. package/src/components/PathBreadcrumbs/styles.module.scss +4 -0
  54. package/src/constants.ts +2 -0
  55. package/dist/cjs/components/MainMenu/utils/getAvatarNameFromLabel.d.ts +0 -14
  56. package/dist/cjs/components/MainMenu/utils/getAvatarNameFromLabel.js +0 -24
  57. package/dist/esm/components/MainMenu/utils/getAvatarNameFromLabel.d.ts +0 -14
  58. package/dist/esm/components/MainMenu/utils/getAvatarNameFromLabel.js +0 -21
  59. package/src/components/MainMenu/utils/getAvatarNameFromLabel.ts +0 -24
@@ -19,8 +19,7 @@ export function Content({ searchValue, search, rightTop, segments, searchGroups
19
19
  const { t } = headerLocale.useTranslations();
20
20
  const showDescription = useDeferredValue(preferences?.showDescription.value ?? false);
21
21
  const segmentPrefsById = useMemo(() => new Map((segmentPrefs ?? []).map(prefs => [prefs.id, prefs])), [segmentPrefs]);
22
- const visibleSegments = useMemo(() => segments.filter(segment => segment.items.some(group => getLinksGroupVisibleItemsCount(group) > 0)), [segments]);
23
- const defaultSegmentId = visibleSegments[0]?.id ?? segments[0]?.id ?? '';
22
+ const defaultSegmentId = activeSegmentId ?? segments[0]?.id ?? '';
24
23
  const [segmentId = defaultSegmentId, setSegmentId] = useValueControl({
25
24
  value: activeSegmentId,
26
25
  defaultValue: defaultSegmentId,
@@ -29,10 +28,10 @@ export function Content({ searchValue, search, rightTop, segments, searchGroups
29
28
  const isSearching = Boolean(searchValue);
30
29
  const enableServiceDrag = Boolean(favorite) && !isMobile && !isSearching;
31
30
  useEffect(() => {
32
- if (!visibleSegments.some(segment => segment.id === segmentId) && defaultSegmentId) {
31
+ if (!segments.some(segment => segment.id === segmentId) && defaultSegmentId) {
33
32
  setSegmentId(defaultSegmentId);
34
33
  }
35
- }, [defaultSegmentId, segmentId, setSegmentId, visibleSegments]);
34
+ }, [defaultSegmentId, segmentId, setSegmentId, segments]);
36
35
  const handleLinkClick = useCallback(({ disabled, onClick }, e) => {
37
36
  if (disabled) {
38
37
  e?.preventDefault();
@@ -73,23 +72,23 @@ export function Content({ searchValue, search, rightTop, segments, searchGroups
73
72
  }
74
73
  onExpandedChange([...new Set([...expandedIds, ...visibleIds])]);
75
74
  }, [allGroupsExpanded, expandedIds, onExpandedChange, visibleGroups]);
76
- const segmentItems = useMemo(() => visibleSegments.map(segment => ({
75
+ const segmentItems = useMemo(() => segments.map(segment => ({
77
76
  value: segment.id,
78
77
  label: segment.label,
79
78
  icon: segment.icon,
80
- })), [visibleSegments]);
79
+ })), [segments]);
81
80
  const hasCards = visibleGroups.length > 0;
82
81
  const cards = (() => {
83
82
  if (loading) {
84
83
  return Array.from({ length: 5 }).map((_, index) => _jsx(SortableGroupSkeleton, { isMobile: isMobile }, index));
85
84
  }
86
85
  if (hasCards) {
87
- return (_jsx(SortableContext, { items: visibleGroups.map(({ id }) => id), strategy: verticalListSortingStrategy, children: _jsx(Accordion, { selectionMode: 'multiple', expanded: expandedIds, onExpandedChange: onExpandedChange, children: visibleGroups.map(({ id, label, items, favoritesEnabled = true, blockColor, highlight }) => (_jsx(SortableGroup, { id: id, label: label, items: items, isExpanded: expandedIds.includes(id), blockColor: resolveGroupBlockColor(blockColor, preferences?.showGroupsColors?.value), showDescription: showDescription, isMobile: isMobile, enableServiceDrag: enableServiceDrag, favorite: favoritesEnabled ? favorite : undefined, onServiceClick: handleLinkClick, highlight: highlight }, id))) }) }));
86
+ return (_jsx(SortableContext, { items: visibleGroups.map(({ id }) => id), strategy: verticalListSortingStrategy, children: _jsx(Accordion, { selectionMode: 'multiple', expanded: expandedIds, onExpandedChange: onExpandedChange, children: visibleGroups.map(({ id, label, icon, items, favoritesEnabled = true, blockColor, highlight }) => (_jsx(SortableGroup, { id: id, icon: icon, label: label, items: items, isExpanded: expandedIds.includes(id), blockColor: resolveGroupBlockColor(blockColor, preferences?.showGroupsColors?.value), showDescription: showDescription, isMobile: isMobile, enableServiceDrag: enableServiceDrag, favorite: favoritesEnabled ? favorite : undefined, onServiceClick: handleLinkClick, highlight: highlight }, id))) }) }));
88
87
  }
89
88
  return (_jsx(InfoBlock, { size: 'm', icon: {
90
89
  icon: SearchSVG,
91
90
  appearance: 'neutral',
92
91
  }, content: isSearching ? t('noDataFound') : t('noData'), "data-test-id": isSearching ? TEST_IDS.noDataFound : TEST_IDS.noData }));
93
92
  })();
94
- return (_jsxs(_Fragment, { children: [search, _jsxs("div", { className: cn(styles.content, className), "data-mobile": isMobile || undefined, "data-empty": !hasCards || undefined, children: [!isSearching && (_jsxs(_Fragment, { children: [isMobile && rightTop, _jsx(ContentToolbar, { segment: segmentId, onSegmentChange: setSegmentId, segmentItems: segmentItems, allGroupsExpanded: allGroupsExpanded, onToggleAllGroupsExpanded: handleToggleAllGroupsExpanded, preferences: preferences, isMobile: isMobile }), !isMobile && rightTop] })), cards, footer] })] }));
93
+ return (_jsxs(_Fragment, { children: [search, _jsxs("div", { className: cn(styles.content, className), "data-mobile": isMobile || undefined, "data-empty": (!loading && !hasCards) || undefined, children: [!isSearching && (_jsxs(_Fragment, { children: [isMobile && rightTop, _jsx(ContentToolbar, { segment: segmentId, onSegmentChange: setSegmentId, segmentItems: segmentItems, allGroupsExpanded: allGroupsExpanded, onToggleAllGroupsExpanded: handleToggleAllGroupsExpanded, preferences: preferences, isMobile: isMobile }), !isMobile && rightTop] })), cards, footer] })] }));
95
94
  }
@@ -3,6 +3,7 @@
3
3
  gap:var(--sn-primitive-dimension-8, 8px);
4
4
  align-items:center;
5
5
  justify-content:space-between;
6
+ width:100%;
6
7
  min-width:0;
7
8
  min-height:var(--sn-primitive-dimension-32, 32px);
8
9
  }
@@ -1,11 +1,11 @@
1
- import { LinksGroupBlockColor, LinksGroupTitle } from '../../../../types.js';
1
+ import { LinksGroup, LinksGroupBlockColor, LinksGroupTitle } from '../../../../types.js';
2
2
  import { SortableGroupCardsProps } from './components/index.js';
3
- export type SortableGroupProps = Omit<SortableGroupCardsProps, 'groupId'> & {
3
+ export type SortableGroupProps = Omit<SortableGroupCardsProps, 'groupId'> & Pick<LinksGroup, 'icon'> & {
4
4
  id: string;
5
5
  label: LinksGroupTitle;
6
6
  isExpanded?: boolean;
7
7
  blockColor?: LinksGroupBlockColor;
8
8
  highlight?: boolean;
9
9
  };
10
- export declare function SortableGroup({ id, label, items, isExpanded, showDescription, isMobile, enableServiceDrag, favorite, onServiceClick, blockColor, highlight, }: SortableGroupProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare function SortableGroup({ id, icon, label, items, isExpanded, showDescription, isMobile, enableServiceDrag, favorite, onServiceClick, blockColor, highlight, }: SortableGroupProps): import("react/jsx-runtime").JSX.Element;
11
11
  export declare function SortableGroupDragPreview({ id, label, items, isExpanded, showDescription, isMobile, favorite, blockColor, highlight, }: Omit<SortableGroupProps, 'enableServiceDrag'>): import("react/jsx-runtime").JSX.Element;
@@ -7,7 +7,7 @@ import { useThemeAppearance } from '@cloud-ru/ds-theme';
7
7
  import { TEST_IDS } from '../../constants.js';
8
8
  import { SortableGroupCards, SortableGroupHeader } from './components/index.js';
9
9
  import styles from './styles.module.css';
10
- export function SortableGroup({ id, label, items, isExpanded, showDescription, isMobile, enableServiceDrag, favorite, onServiceClick, blockColor, highlight, }) {
10
+ export function SortableGroup({ id, icon, label, items, isExpanded, showDescription, isMobile, enableServiceDrag, favorite, onServiceClick, blockColor, highlight, }) {
11
11
  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
12
12
  id,
13
13
  disabled: !enableServiceDrag,
@@ -21,7 +21,7 @@ export function SortableGroup({ id, label, items, isExpanded, showDescription, i
21
21
  transition,
22
22
  };
23
23
  const { colorScheme } = useThemeAppearance().appearance;
24
- return (_jsxs(DragGhost, { innerRef: setNodeRef, id: id, style: style, className: styles.group, dragging: isDragging, mode: DRAG_MODE.Dynamic, "data-test-id": `${TEST_IDS.groupCard}-${id}`, children: [_jsx("div", { className: styles.decoration, "data-color-scheme": colorScheme, "data-is-highlighted": highlight || undefined, children: _jsx("div", { className: styles.decorationBackground, children: _jsx("div", { className: styles.colorMarker, "data-block-color": blockColor }) }) }), _jsx(Accordion.CollapseBlockTertiary, { id: id, showChevron: false, afterTitle: _jsx(SortableGroupHeader, { label: label, isExpanded: isExpanded, enableServiceDrag: enableServiceDrag, attributes: attributes, listeners: listeners, isMobile: isMobile }), children: _jsx(SortableGroupCards, { groupId: id, items: items, showDescription: showDescription, isMobile: isMobile, enableServiceDrag: enableServiceDrag, favorite: favorite, onServiceClick: onServiceClick }) })] }));
24
+ return (_jsxs(DragGhost, { innerRef: setNodeRef, id: id, style: style, className: styles.group, dragging: isDragging, mode: DRAG_MODE.Dynamic, "data-test-id": `${TEST_IDS.groupCard}-${id}`, children: [_jsx("div", { className: styles.decoration, "data-color-scheme": colorScheme, "data-is-highlighted": highlight || undefined, children: _jsx("div", { className: styles.decorationBackground, children: _jsx("div", { className: styles.colorMarker, "data-block-color": blockColor }) }) }), _jsx(Accordion.CollapseBlockTertiary, { id: id, showChevron: false, afterTitle: _jsx(SortableGroupHeader, { label: label, icon: icon, isExpanded: isExpanded, enableServiceDrag: enableServiceDrag, attributes: attributes, listeners: listeners, isMobile: isMobile }), children: _jsx(SortableGroupCards, { groupId: id, items: items, showDescription: showDescription, isMobile: isMobile, enableServiceDrag: enableServiceDrag, favorite: favorite, onServiceClick: onServiceClick }) })] }));
25
25
  }
26
26
  export function SortableGroupDragPreview({ id, label, items, isExpanded, showDescription, isMobile, favorite, blockColor, highlight, }) {
27
27
  const { colorScheme } = useThemeAppearance().appearance;
@@ -1,9 +1,9 @@
1
- import { LinksGroupTitle } from '../../../../../types.js';
1
+ import { LinksGroup, LinksGroupTitle } from '../../../../../types.js';
2
2
  import { SortableGroupDragHandleProps } from './SortableGroupDragHandle.js';
3
- export type SortableGroupHeaderProps = {
3
+ export type SortableGroupHeaderProps = Pick<LinksGroup, 'icon'> & {
4
4
  label: LinksGroupTitle;
5
5
  isExpanded?: boolean;
6
6
  enableServiceDrag?: boolean;
7
7
  isMobile?: boolean;
8
8
  } & SortableGroupDragHandleProps;
9
- export declare function SortableGroupHeader({ label, isExpanded, enableServiceDrag, attributes, listeners, isMobile, }: SortableGroupHeaderProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function SortableGroupHeader({ label, icon, isExpanded, enableServiceDrag, attributes, listeners, isMobile, }: SortableGroupHeaderProps): import("react/jsx-runtime").JSX.Element;
@@ -2,8 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Button } from '@cloud-ru/ds-button';
3
3
  import { ChevronDownSVG, ChevronUpSVG } from '@cloud-ru/ds-icons/interface/system';
4
4
  import { Typography } from '@cloud-ru/ds-typography';
5
+ import { TitleClickable } from '@cloud-ru/ds-uikit-product-title-clickable';
6
+ import { getLinkEmblem } from '../../../../../utils/index.js';
5
7
  import styles from '../styles.module.css';
6
8
  import { SortableGroupDragHandle } from './SortableGroupDragHandle.js';
7
- export function SortableGroupHeader({ label, isExpanded, enableServiceDrag, attributes, listeners, isMobile, }) {
8
- return (_jsxs("div", { className: styles.header, "data-expanded": isExpanded || undefined, children: [_jsx(Typography, { variant: 'title', size: 'm', className: styles.headerTitle, children: label.text }), (enableServiceDrag || !isMobile) && (_jsxs("div", { className: styles.headerActions, "data-always-visible": isMobile || undefined, children: [enableServiceDrag && _jsx(SortableGroupDragHandle, { attributes: attributes, listeners: listeners }), !isMobile && (_jsx(Button, { view: 'elevated', appearance: 'neutral', size: 's', icon: isExpanded ? _jsx(ChevronUpSVG, {}) : _jsx(ChevronDownSVG, {}), "data-test-id": 'header__drawer-menu__group-card-collapse' }))] }))] }));
9
+ export function SortableGroupHeader({ label, icon, isExpanded, enableServiceDrag, attributes, listeners, isMobile, }) {
10
+ const handleLabelClick = e => {
11
+ e.stopPropagation();
12
+ label?.onClick?.(e);
13
+ };
14
+ return (_jsxs("div", { className: styles.header, "data-expanded": isExpanded || undefined, children: [_jsx(Typography, { variant: 'title', size: 'm', className: styles.headerTitle, "data-test-id": 'header__drawer-menu__group-card-title', children: label.onClick || label.href ? (_jsx(TitleClickable, { ...(label.href ? { as: 'a', href: label.href } : { as: 'div' }), onClick: handleLabelClick, title: label.text, before: getLinkEmblem({ icon }) })) : (label.text) }), (enableServiceDrag || !isMobile) && (_jsxs("div", { className: styles.headerActions, "data-always-visible": isMobile || undefined, children: [enableServiceDrag && _jsx(SortableGroupDragHandle, { attributes: attributes, listeners: listeners }), !isMobile && (_jsx(Button, { view: 'elevated', appearance: 'neutral', size: 's', icon: isExpanded ? _jsx(ChevronUpSVG, {}) : _jsx(ChevronDownSVG, {}), "data-test-id": 'header__drawer-menu__group-card-collapse' }))] }))] }));
9
15
  }
@@ -50,6 +50,7 @@
50
50
  overflow-x:clip;
51
51
  display:flex;
52
52
  flex-direction:column;
53
+ width:100%;
53
54
  min-width:0;
54
55
  padding:var(--sn-primitive-dimension-4, 4px) var(--sn-primitive-dimension-8, 8px) var(--sn-primitive-dimension-8, 8px) var(--sn-primitive-dimension-12, 12px);
55
56
  border-radius:var(--sn-density-radius-2xs, var(--sn-primitive-dimension-4, 4px));
@@ -1,2 +1,2 @@
1
1
  import { MainMenuProps } from '../../types.js';
2
- export declare function MenuDesktop({ open: openProp, setOpen: setOpenProp, settingItems, platformGroups, segments, segmentPrefs, activeSegmentId, onActiveSegmentChange, onSegmentOrderChange, onSegmentExpandedChange, favorite, search, preferences, logo, rightTop, leftTop, defaultWidth, onWidthChangeEnd, draggerTooltip, loading, }: MainMenuProps): import("react/jsx-runtime").JSX.Element;
2
+ export declare function MenuDesktop({ open: openProp, setOpen: setOpenProp, settingItems, platformGroups, segments, segmentPrefs, activeSegmentId, onActiveSegmentChange, onSegmentOrderChange, onSegmentExpandedChange, favorite, search, preferences, logo, rightTop, leftTop, leftBottom, defaultWidth, onWidthChangeEnd, draggerTooltip, loading, }: MainMenuProps): import("react/jsx-runtime").JSX.Element;
@@ -1,13 +1,15 @@
1
+ import { createElement as _createElement } from "react";
1
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
3
  import { Divider } from '@cloud-ru/ds-divider';
3
4
  import { DrawerCustom } from '@cloud-ru/ds-drawer';
4
- import { List } from '@cloud-ru/ds-list';
5
5
  import { Scroll } from '@cloud-ru/ds-scroll';
6
+ import { CardServiceLight } from '@cloud-ru/ds-uikit-product-card-predefined';
6
7
  import { useValueControl } from '@cloud-ru/ds-utils';
7
8
  import { useCallback, useEffect, useMemo } from 'react';
8
9
  import { TEST_IDS } from '../../../../constants.js';
9
10
  import { MainMenuDndContext, useMainMenuDnd } from '../../hooks/useMainMenuDnd.js';
10
11
  import { useMenuItems } from '../../hooks/useMenuItems.js';
12
+ import { getLinkEmblem } from '../../utils/index.js';
11
13
  import { Content } from '../Content/index.js';
12
14
  import { Favorites } from '../Favorites/index.js';
13
15
  import { MenuHeaderBrand } from '../MenuHeaderBrand/index.js';
@@ -15,7 +17,7 @@ import { MountAnimation } from '../MountAnimation/index.js';
15
17
  import { Search } from '../Search/index.js';
16
18
  import { MENU_WIDTH_MAX, MENU_WIDTH_MIN } from './constants.js';
17
19
  import styles from './styles.module.css';
18
- export function MenuDesktop({ open: openProp, setOpen: setOpenProp, settingItems, platformGroups, segments, segmentPrefs, activeSegmentId, onActiveSegmentChange, onSegmentOrderChange, onSegmentExpandedChange, favorite, search, preferences, logo, rightTop, leftTop, defaultWidth = MENU_WIDTH_MAX, onWidthChangeEnd, draggerTooltip, loading, }) {
20
+ export function MenuDesktop({ open: openProp, setOpen: setOpenProp, settingItems, platformGroups, segments, segmentPrefs, activeSegmentId, onActiveSegmentChange, onSegmentOrderChange, onSegmentExpandedChange, favorite, search, preferences, logo, rightTop, leftTop, leftBottom, defaultWidth = MENU_WIDTH_MAX, onWidthChangeEnd, draggerTooltip, loading, }) {
19
21
  const [open = false, setOpen] = useValueControl({ value: openProp, onChange: setOpenProp });
20
22
  const { scrollRef, searchRef, resultItems } = useMenuItems({
21
23
  segments,
@@ -35,6 +37,7 @@ export function MenuDesktop({ open: openProp, setOpen: setOpenProp, settingItems
35
37
  return () => cancelAnimationFrame(frameId);
36
38
  }, [open, hasSearch, searchRef]);
37
39
  const isNeedRightBlock = segments && segments.some(segment => segment.items.length > 0);
40
+ const hasBottomItems = Boolean(settingItems?.length) || Boolean(leftBottom);
38
41
  const allServiceGroups = useMemo(() => segments?.flatMap(segment => segment.items) ?? [], [segments]);
39
42
  const mainMenuDnd = useMainMenuDnd({
40
43
  favorite,
@@ -44,7 +47,7 @@ export function MenuDesktop({ open: openProp, setOpen: setOpenProp, settingItems
44
47
  const handleCloseDrawer = useCallback(() => {
45
48
  setOpen(false);
46
49
  }, [setOpen]);
47
- const menu = (_jsxs("div", { className: styles.menu, children: [_jsxs("div", { className: styles.menuHeader, children: [_jsx(MenuHeaderBrand, { logo: logo, onClose: handleCloseDrawer, className: styles.menuHeaderBrand }), isNeedRightBlock && search && (_jsxs(_Fragment, { children: [_jsx(Divider, { orientation: 'vertical' }), _jsx(Search, { ...search, ref: searchRef })] }))] }), _jsx(Divider, {}), _jsxs("div", { className: styles.menuBody, children: [_jsxs(MountAnimation, { className: styles.left, "data-test-id": TEST_IDS.mainMenu.left, type: 'slide-right', children: [leftTop && _jsx("div", { className: styles.leftTop, children: leftTop }), isNeedRightBlock && favorite && _jsx(Favorites, { favorite: favorite, allServiceGroups: allServiceGroups }), Boolean(settingItems?.length) && (_jsx(List, { items: settingItems ?? [], size: 'm', barHideStrategy: 'never', className: styles.settingItems }))] }), isNeedRightBlock && (_jsxs(_Fragment, { children: [_jsx(MountAnimation, { type: 'fade-in', children: _jsx(Divider, { orientation: 'vertical' }) }), _jsx(MountAnimation, { type: 'fade-slide-up-right', className: styles.right, "data-test-id": TEST_IDS.mainMenu.right, children: _jsx(Scroll, { paddingAbsolute: true, className: styles.scroll, ref: scrollRef, barHideStrategy: 'never', overflow: { x: 'hidden' }, children: _jsx(Content, { onClose: handleCloseDrawer, className: styles.rightContent, searchValue: search?.value, rightTop: rightTop, favorite: favorite, segments: segments, searchGroups: resultItems, segmentPrefs: segmentPrefs, activeSegmentId: activeSegmentId, onActiveSegmentChange: onActiveSegmentChange, onSegmentOrderChange: onSegmentOrderChange, onSegmentExpandedChange: onSegmentExpandedChange, preferences: preferences, loading: loading }) }) })] }))] })] }));
50
+ const menu = (_jsxs("div", { className: styles.menu, children: [_jsxs("div", { className: styles.menuHeader, children: [_jsx(MenuHeaderBrand, { logo: logo, onClose: handleCloseDrawer, className: styles.menuHeaderBrand }), isNeedRightBlock && search && (_jsxs(_Fragment, { children: [_jsx(Divider, { orientation: 'vertical' }), _jsx(Search, { ...search, ref: searchRef })] }))] }), _jsx(Divider, {}), _jsxs("div", { className: styles.menuBody, children: [_jsxs(MountAnimation, { className: styles.left, "data-test-id": TEST_IDS.mainMenu.left, type: 'slide-right', children: [leftTop && _jsx("div", { className: styles.leftTop, children: leftTop }), isNeedRightBlock && favorite && _jsx(Favorites, { favorite: favorite, allServiceGroups: allServiceGroups }), hasBottomItems ? (_jsxs("div", { className: styles.leftBottom, "data-test-id": TEST_IDS.mainMenu.leftBottom, children: [_jsx("div", { className: styles.leftBottomItems, "data-test-id": TEST_IDS.mainMenu.leftBottomItems, children: settingItems?.map(item => (_jsxs(_Fragment, { children: [item.dividerBefore && _jsx(Divider, { orientation: 'horizontal', className: styles.leftBottomDivider }), _createElement(CardServiceLight, { ...(item.href ? { href: item.href, as: 'a' } : { as: 'button', type: 'button' }), key: item.id, title: item.label, icon: getLinkEmblem(item), onClick: item.onClick, "data-test-id": `header__drawer-menu__setting-${item.id}` })] }))) }), leftBottom] })) : null] }), isNeedRightBlock && (_jsxs(_Fragment, { children: [_jsx(MountAnimation, { type: 'fade-in', children: _jsx(Divider, { orientation: 'vertical' }) }), _jsx(MountAnimation, { type: 'fade-slide-up-right', className: styles.right, "data-test-id": TEST_IDS.mainMenu.right, children: _jsx(Scroll, { paddingAbsolute: true, className: styles.scroll, ref: scrollRef, barHideStrategy: 'never', overflow: { x: 'hidden' }, children: _jsx(Content, { onClose: handleCloseDrawer, className: styles.rightContent, searchValue: search?.value, rightTop: rightTop, favorite: favorite, segments: segments, searchGroups: resultItems, segmentPrefs: segmentPrefs, activeSegmentId: activeSegmentId, onActiveSegmentChange: onActiveSegmentChange, onSegmentOrderChange: onSegmentOrderChange, onSegmentExpandedChange: onSegmentExpandedChange, preferences: preferences, loading: loading }) }) })] }))] })] }));
48
51
  return (_jsx(DrawerCustom, { open: open, onClose: handleCloseDrawer, width: isNeedRightBlock ? 'm' : 's', position: 'left', className: styles.drawer, rootClassName: styles.drawerRoot, "data-test-id": TEST_IDS.mainMenu.drawer, "data-small": !isNeedRightBlock || undefined, disableMotions: true, showButtonClosed: false, resizable: isNeedRightBlock
49
52
  ? {
50
53
  min: MENU_WIDTH_MIN,
@@ -43,9 +43,18 @@
43
43
  min-height:0;
44
44
  }
45
45
 
46
- .settingItems{
47
- flex-shrink:0;
48
- height:auto;
46
+ .leftBottomItems{
47
+ width:100%;
48
+ }
49
+
50
+ .leftBottom{
51
+ display:flex;
52
+ flex-direction:column;
53
+ gap:var(--sn-primitive-dimension-8, 8px);
54
+ }
55
+
56
+ .leftBottomDivider{
57
+ margin:var(--sn-primitive-dimension-2, 2px) 0;
49
58
  }
50
59
 
51
60
  .right{
@@ -63,6 +72,7 @@
63
72
  display:flex;
64
73
  flex-direction:column;
65
74
  gap:var(--sn-primitive-dimension-4, 4px);
75
+ width:100%;
66
76
  min-width:0;
67
77
  padding:var(--sn-primitive-dimension-16, 16px);
68
78
  }
@@ -1,4 +1,4 @@
1
- import { BaseItemProps, ListProps } from '@cloud-ru/ds-list';
1
+ import { BaseItemProps } from '@cloud-ru/ds-list';
2
2
  import { SearchProps as SearchPropsSnack } from '@cloud-ru/ds-search';
3
3
  import { CardServiceLightProps } from '@cloud-ru/ds-uikit-product-card-predefined';
4
4
  import { JSXElementConstructor, MouseEvent, ReactNode } from 'react';
@@ -19,12 +19,7 @@ export type InnerLink = {
19
19
  * остаётся только первое по приоритету сегментов (см. {@link MainMenuProps.segments}).
20
20
  */
21
21
  id: string;
22
- /**
23
- * Иконка карточки.
24
- *
25
- * Если не задана, вместо иконки рендерится `Avatar` с двухбуквенным текстом по `label`
26
- * (см. `getAvatarNameFromLabel`).
27
- */
22
+ /** Иконка карточки. */
28
23
  icon?: JSXElementConstructor<{
29
24
  size?: number;
30
25
  className?: string;
@@ -53,9 +48,11 @@ export type InnerLink = {
53
48
  type TitleStatic = {
54
49
  text: string;
55
50
  onClick?: never;
51
+ href?: never;
56
52
  };
57
53
  type TitleClickable = {
58
54
  text: string;
55
+ href?: string;
59
56
  onClick?(e?: MouseEvent<HTMLElement>): void;
60
57
  };
61
58
  export type LinksGroupTitle = TitleStatic | TitleClickable;
@@ -71,6 +68,10 @@ export type LinksGroup = {
71
68
  id: string;
72
69
  /** Заголовок группы в сетке карточек и в боковой навигации. */
73
70
  label: LinksGroupTitle;
71
+ icon?: JSXElementConstructor<{
72
+ size?: number;
73
+ className?: string;
74
+ }>;
74
75
  /**
75
76
  * Синонимы заголовка группы для fuzzy-поиска.
76
77
  */
@@ -202,12 +203,16 @@ export type MainMenuPreferencesProps = {
202
203
  */
203
204
  showGroupsColors?: MainMenuToggleProps;
204
205
  };
206
+ export type MainMenuSettingsItem = Pick<InnerLink, 'id' | 'label' | 'icon' | 'onClick' | 'href' | 'hidden'> & {
207
+ dividerBefore?: boolean;
208
+ };
205
209
  export type MainMenuProps = {
206
210
  disabled?: boolean;
207
211
  open?: boolean;
208
212
  setOpen?(open: boolean): void;
209
213
  logo?: ReactNode;
210
214
  leftTop?: ReactNode;
215
+ leftBottom?: ReactNode;
211
216
  rightTop?: ReactNode;
212
217
  /**
213
218
  * Сегменты правой панели (сетка карточек) — только каталог.
@@ -245,10 +250,10 @@ export type MainMenuProps = {
245
250
  /**
246
251
  * Пункты левой колонки (desktop) / нижней части списка (mobile).
247
252
  *
248
- * Плоский список `List` (`BaseItem` и при необходимости `type: 'group'` с `divider` для разделителей).
253
+ * Плоский список (`dividerBefore` для разделителей).
249
254
  * Не связан с сегментами правой панели и не меняется при сортировке групп в сегментах.
250
255
  */
251
- settingItems?: ListProps['items'];
256
+ settingItems?: MainMenuSettingsItem[];
252
257
  /**
253
258
  * Платформенные группы (например «Облачные продукты», «Другие продукты»).
254
259
  *
@@ -1,3 +1,3 @@
1
1
  import { CardServiceLightProps } from '@cloud-ru/ds-uikit-product-card-predefined';
2
2
  import { InnerLink } from '../types.js';
3
- export declare function getLinkEmblem(link: InnerLink): CardServiceLightProps['icon'];
3
+ export declare function getLinkEmblem(link: Pick<InnerLink, 'icon'>): CardServiceLightProps['icon'];
@@ -1,15 +1,7 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Avatar } from '@cloud-ru/ds-avatar';
3
1
  import { createElement } from 'react';
4
- import { getAvatarNameFromLabel } from './getAvatarNameFromLabel.js';
5
- function createAvatarIcon(displayName) {
6
- return function LinkAvatarIcon({ className }) {
7
- return (_jsx(Avatar, { name: displayName, showTwoSymbols: true, shape: 'squared', appearance: 'neutral', size: 's', className: className }));
8
- };
9
- }
10
2
  export function getLinkEmblem(link) {
11
3
  if (!link.icon) {
12
- return createElement(createAvatarIcon(getAvatarNameFromLabel(link.label)));
4
+ return undefined;
13
5
  }
14
6
  return createElement(link.icon);
15
7
  }
@@ -1,5 +1,4 @@
1
1
  export * from './innerLink.js';
2
- export * from './getAvatarNameFromLabel.js';
3
2
  export * from './flatLinksGroups.js';
4
3
  export * from './getLinkEmblem.js';
5
4
  export * from './mapInnerLinksToListItems.js';
@@ -1,5 +1,4 @@
1
1
  export * from './innerLink.js';
2
- export * from './getAvatarNameFromLabel.js';
3
2
  export * from './flatLinksGroups.js';
4
3
  export * from './getLinkEmblem.js';
5
4
  export * from './mapInnerLinksToListItems.js';
@@ -1,4 +1,7 @@
1
1
  .breadcrumbs{
2
+ display:flex;
3
+ align-items:center;
2
4
  min-width:0;
5
+ height:100%;
3
6
  padding:0 var(--sn-primitive-dimension-8, 8px) !important;
4
7
  }
@@ -10,6 +10,8 @@ export declare const TEST_IDS: {
10
10
  readonly search: "header__drawer-menu__search";
11
11
  readonly left: "header__drawer-menu__left";
12
12
  readonly right: "header__drawer-menu__right";
13
+ readonly leftBottom: "header__drawer-menu__left-bottom";
14
+ readonly leftBottomItems: "header__drawer-menu__left-bottom-items";
13
15
  };
14
16
  readonly userMenu: {
15
17
  readonly root: "header__user-menu";
@@ -10,6 +10,8 @@ export const TEST_IDS = {
10
10
  search: 'header__drawer-menu__search',
11
11
  left: 'header__drawer-menu__left',
12
12
  right: 'header__drawer-menu__right',
13
+ leftBottom: 'header__drawer-menu__left-bottom',
14
+ leftBottomItems: 'header__drawer-menu__left-bottom-items',
13
15
  },
14
16
  userMenu: {
15
17
  root: 'header__user-menu',
@@ -157,6 +157,7 @@
157
157
  gap:var(--sn-primitive-dimension-8, 8px);
158
158
  align-items:center;
159
159
  justify-content:space-between;
160
+ width:100%;
160
161
  min-width:0;
161
162
  min-height:var(--sn-primitive-dimension-32, 32px);
162
163
  }
@@ -297,6 +298,7 @@
297
298
  overflow-x:clip;
298
299
  display:flex;
299
300
  flex-direction:column;
301
+ width:100%;
300
302
  min-width:0;
301
303
  padding:var(--sn-primitive-dimension-4, 4px) var(--sn-primitive-dimension-8, 8px) var(--sn-primitive-dimension-8, 8px) var(--sn-primitive-dimension-12, 12px);
302
304
  border-radius:var(--sn-density-radius-2xs, var(--sn-primitive-dimension-4, 4px));
@@ -646,9 +648,18 @@
646
648
  min-height:0;
647
649
  }
648
650
 
649
- .settingItems{
650
- flex-shrink:0;
651
- height:auto;
651
+ .leftBottomItems{
652
+ width:100%;
653
+ }
654
+
655
+ .leftBottom{
656
+ display:flex;
657
+ flex-direction:column;
658
+ gap:var(--sn-primitive-dimension-8, 8px);
659
+ }
660
+
661
+ .leftBottomDivider{
662
+ margin:var(--sn-primitive-dimension-2, 2px) 0;
652
663
  }
653
664
 
654
665
  .right{
@@ -666,6 +677,7 @@
666
677
  display:flex;
667
678
  flex-direction:column;
668
679
  gap:var(--sn-primitive-dimension-4, 4px);
680
+ width:100%;
669
681
  min-width:0;
670
682
  padding:var(--sn-primitive-dimension-16, 16px);
671
683
  }
@@ -827,7 +839,10 @@
827
839
  min-width:0;
828
840
  }
829
841
  .breadcrumbs{
842
+ display:flex;
843
+ align-items:center;
830
844
  min-width:0;
845
+ height:100%;
831
846
  padding:0 var(--sn-primitive-dimension-8, 8px) !important;
832
847
  }
833
848
  .container{