@cloud-ru/ds-uikit-product-header 2.1.0 → 2.1.2
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.
- package/CHANGELOG.md +13 -0
- package/README.md +6 -6
- package/dist/cjs/components/HeaderLayout/styles.module.css +1 -0
- package/dist/cjs/components/MainMenu/helperComponents/Banners/MenuBanner.js +3 -1
- package/dist/cjs/components/MainMenu/helperComponents/Banners/styles.module.css +10 -0
- package/dist/cjs/components/UserMenu/UserMenu.d.ts +5 -1
- package/dist/cjs/components/UserMenu/hooks/useUserMenuItems.js +4 -0
- package/dist/cjs/style.css +11 -0
- package/dist/esm/components/HeaderLayout/styles.module.css +1 -0
- package/dist/esm/components/MainMenu/helperComponents/Banners/MenuBanner.js +3 -1
- package/dist/esm/components/MainMenu/helperComponents/Banners/styles.module.css +10 -0
- package/dist/esm/components/UserMenu/UserMenu.d.ts +5 -1
- package/dist/esm/components/UserMenu/hooks/useUserMenuItems.js +4 -0
- package/dist/esm/style.css +11 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/src/components/HeaderLayout/styles.module.scss +1 -0
- package/src/components/MainMenu/helperComponents/Banners/MenuBanner.tsx +4 -1
- package/src/components/MainMenu/helperComponents/Banners/styles.module.scss +16 -0
- package/src/components/UserMenu/UserMenu.tsx +5 -1
- package/src/components/UserMenu/hooks/useUserMenuItems.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 2.1.2 (2026-10-05)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **FF-9172:** allow header breadcrumbs wrapper to shrink ([12a0633](https://github.com/cloud-ru-tech/snack-v2/commit/12a0633f85980128a14af6c467f9cfd7566e395e))
|
|
11
|
+
- **FF-9172:** gen-props resolves indexed access to the field type ([baf31c4](https://github.com/cloud-ru-tech/snack-v2/commit/baf31c4053e76c81d681c9c19a6622f009f40413))
|
|
12
|
+
- **FF-9172:** keep menu banner close button in flow and always visible on mobile ([ad48053](https://github.com/cloud-ru-tech/snack-v2/commit/ad480531898d9ab8f75f2aba2a066afe233e04da))
|
|
13
|
+
- **FF-9172:** render user menu as single list when no organizations passed ([363ca6a](https://github.com/cloud-ru-tech/snack-v2/commit/363ca6abfc44575c45fd2687e6aa4bf1608ed70f))
|
|
14
|
+
|
|
15
|
+
## 2.1.1 (2026-10-05)
|
|
16
|
+
|
|
17
|
+
**Note:** Version bump only for package @ds/uikit-product-header
|
|
18
|
+
|
|
6
19
|
# 2.1.0 (2026-10-05)
|
|
7
20
|
|
|
8
21
|
### Features
|
package/README.md
CHANGED
|
@@ -339,7 +339,7 @@ function LocalMainMenu() {
|
|
|
339
339
|
| Prop | Type | Default | Description |
|
|
340
340
|
|------|------|---------|-------------|
|
|
341
341
|
| `aliases` | `string[]` | — | Синонимы для fuzzy-поиска. |
|
|
342
|
-
| `badge` | `
|
|
342
|
+
| `badge` | `CardPromoTagProps` \| `PromoTagPredefinedBaseProps` | — | Промо-тег карточки. |
|
|
343
343
|
| `description` | `string \| undefined` | — | Краткое описание сервиса — отображается при включённом переключателе «Описание». |
|
|
344
344
|
| `disabled` | `boolean \| undefined` | — | Карточка недоступна для клика/drag и визуально приглушена. |
|
|
345
345
|
| `favoritesEnabled` | `boolean \| undefined` | — | Разрешено ли добавление карточки в избранное. default=true - разрешено |
|
|
@@ -513,7 +513,7 @@ import { MainMenu, MenuMobile } from '@cloud-ru/ds-uikit-product-header';
|
|
|
513
513
|
| Prop | Type | Default | Description |
|
|
514
514
|
|------|------|---------|-------------|
|
|
515
515
|
| `aliases` | `string[]` | — | Синонимы для fuzzy-поиска. |
|
|
516
|
-
| `badge` | `
|
|
516
|
+
| `badge` | `CardPromoTagProps` \| `PromoTagPredefinedBaseProps` | — | Промо-тег карточки. |
|
|
517
517
|
| `description` | `string \| undefined` | — | Краткое описание сервиса — отображается при включённом переключателе «Описание». |
|
|
518
518
|
| `disabled` | `boolean \| undefined` | — | Карточка недоступна для клика/drag и визуально приглушена. |
|
|
519
519
|
| `favoritesEnabled` | `boolean \| undefined` | — | Разрешено ли добавление карточки в избранное. default=true - разрешено |
|
|
@@ -693,16 +693,16 @@ export function ControlledTheme() {
|
|
|
693
693
|
|
|
694
694
|
| Prop | Type | Default | Description |
|
|
695
695
|
|------|------|---------|-------------|
|
|
696
|
-
| `bottomItems` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `
|
|
696
|
+
| `bottomItems` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `Item` \| `ScrollProps` | — | Пункты перед «Выйти из аккаунта». На desktop закреплены снизу |
|
|
697
697
|
| `onClick` | `(() => void)` | — | Колбэк клика по кнопке-триггеру. |
|
|
698
698
|
| `onLogout` | `(() => void)` | — | Колбэк клика по пункту «Выйти». |
|
|
699
699
|
| `open` | `boolean` | — | Открыто ли меню. <br/> Не передано — состояние открытия неуправляемое (меню само переключает себя по клику на кнопку). |
|
|
700
|
-
| `organizationItems` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `
|
|
700
|
+
| `organizationItems` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `Item` \| `ScrollProps` | — | Список организаций. На desktop — единственная прокручиваемая часть меню. <br/> Не передан или пуст — меню отображается единым списком без закреплённых частей. |
|
|
701
701
|
| `profile` | `UserProfileProps` | `{}` | Профиль пользователя (имя, email, счётчик приглашений). |
|
|
702
702
|
| `setOpen` | `((open: boolean) => void)` | — | Колбэк открытия/закрытия меню. |
|
|
703
703
|
| `settingItems` | `BaseItemProps` | — | Пункты настроек в нижней части меню. |
|
|
704
704
|
| `theme` | `ThemeProps` | — | Переключатель темы в меню. Без пропа не отображается. |
|
|
705
|
-
| `topItems` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `
|
|
705
|
+
| `topItems` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `Item` \| `ScrollProps` | — | Пункты после темы. На desktop закреплены сверху вместе с профилем и темой |
|
|
706
706
|
| `triggerTooltip` | `string` | — | Текст подсказки для кнопки-триггера. |
|
|
707
707
|
|
|
708
708
|
##### Related types
|
|
@@ -823,4 +823,4 @@ export function Basic() {
|
|
|
823
823
|
|
|
824
824
|
| Prop | Type | Default | Description |
|
|
825
825
|
|------|------|---------|-------------|
|
|
826
|
-
| `items` | `
|
|
826
|
+
| `items` | `Item` | — | Пункты хлебных крошек. |
|
|
@@ -12,15 +12,17 @@ const promo_tag_1 = require("@cloud-ru/ds-promo-tag");
|
|
|
12
12
|
const typography_1 = require("@cloud-ru/ds-typography");
|
|
13
13
|
const classnames_1 = __importDefault(require("classnames"));
|
|
14
14
|
const react_1 = require("react");
|
|
15
|
+
const useMobileLayout_1 = require("../../../../hooks/useMobileLayout");
|
|
15
16
|
const locale_1 = require("../../../../locale");
|
|
16
17
|
const constants_1 = require("./constants");
|
|
17
18
|
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
18
19
|
function MenuBanner({ title, promoTag, afterTitle, onClose, className, href = '#', 'data-test-id': dataTestId, ...rest }) {
|
|
19
20
|
const { t } = locale_1.headerLocale.useTranslations();
|
|
21
|
+
const isMobile = (0, useMobileLayout_1.useMobileLayout)();
|
|
20
22
|
const handleClose = (0, react_1.useCallback)(e => {
|
|
21
23
|
e.preventDefault();
|
|
22
24
|
e.stopPropagation();
|
|
23
25
|
onClose?.(e);
|
|
24
26
|
}, [onClose]);
|
|
25
|
-
return ((0, jsx_runtime_1.jsx)(card_1.Card, { as: 'a', href: href, radius: 'l', view: 'simple', className: (0, classnames_1.default)(styles_module_scss_1.default.root, className), "data-test-id": dataTestId ?? constants_1.MENU_BANNER_TEST_IDS.root, ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.contentRow, children: [(0, jsx_runtime_1.jsxs)("span", { className: styles_module_scss_1.default.titleWrapper, children: [(0, jsx_runtime_1.jsx)(typography_1.Typography, { as: 'span', variant: 'body', size: 'm', className: styles_module_scss_1.default.title, "data-test-id": constants_1.MENU_BANNER_TEST_IDS.title, children: title }), promoTag && ((0, jsx_runtime_1.jsx)(promo_tag_1.PromoTag, { ...promoTag, className: styles_module_scss_1.default.promoTag, roleAppearance: promo_tag_1.ROLE_APPEARANCE.Decor, size: promo_tag_1.SIZE.Xs, "data-test-id": constants_1.MENU_BANNER_TEST_IDS.promoTag }))] }), afterTitle && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.afterTitle, "data-test-id": constants_1.MENU_BANNER_TEST_IDS.afterTitle, children: afterTitle })), onClose && ((0, jsx_runtime_1.jsx)(button_1.Button, { className: styles_module_scss_1.default.closeButton, view: 'elevated', appearance: 'neutral', size: 's', icon: (0, jsx_runtime_1.jsx)(system_1.CrossSVG, {}), onClick: handleClose, "aria-label": t('close'), "data-test-id": constants_1.MENU_BANNER_TEST_IDS.close }))] }) }));
|
|
27
|
+
return ((0, jsx_runtime_1.jsx)(card_1.Card, { as: 'a', href: href, radius: 'l', view: 'simple', className: (0, classnames_1.default)(styles_module_scss_1.default.root, className), "data-mobile": isMobile || undefined, "data-test-id": dataTestId ?? constants_1.MENU_BANNER_TEST_IDS.root, ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.contentRow, "data-closable": Boolean(onClose) || undefined, children: [(0, jsx_runtime_1.jsxs)("span", { className: styles_module_scss_1.default.titleWrapper, children: [(0, jsx_runtime_1.jsx)(typography_1.Typography, { as: 'span', variant: 'body', size: 'm', className: styles_module_scss_1.default.title, "data-test-id": constants_1.MENU_BANNER_TEST_IDS.title, children: title }), promoTag && ((0, jsx_runtime_1.jsx)(promo_tag_1.PromoTag, { ...promoTag, className: styles_module_scss_1.default.promoTag, roleAppearance: promo_tag_1.ROLE_APPEARANCE.Decor, size: promo_tag_1.SIZE.Xs, "data-test-id": constants_1.MENU_BANNER_TEST_IDS.promoTag }))] }), afterTitle && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.afterTitle, "data-test-id": constants_1.MENU_BANNER_TEST_IDS.afterTitle, children: afterTitle })), onClose && ((0, jsx_runtime_1.jsx)(button_1.Button, { className: styles_module_scss_1.default.closeButton, view: 'elevated', appearance: 'neutral', size: 's', icon: (0, jsx_runtime_1.jsx)(system_1.CrossSVG, {}), onClick: handleClose, "aria-label": t('close'), "data-test-id": constants_1.MENU_BANNER_TEST_IDS.close }))] }) }));
|
|
26
28
|
}
|
|
@@ -18,6 +18,16 @@
|
|
|
18
18
|
.root:hover .closeButton, .root:focus-visible .closeButton, .root:has(*:focus-visible) .closeButton{
|
|
19
19
|
visibility:visible;
|
|
20
20
|
}
|
|
21
|
+
.root[data-mobile] .closeButton{
|
|
22
|
+
position:relative;
|
|
23
|
+
top:auto;
|
|
24
|
+
right:auto;
|
|
25
|
+
flex-shrink:0;
|
|
26
|
+
visibility:visible;
|
|
27
|
+
}
|
|
28
|
+
.root[data-mobile] .contentRow[data-closable]{
|
|
29
|
+
padding-right:var(--sn-primitive-dimension-4, 4px);
|
|
30
|
+
}
|
|
21
31
|
|
|
22
32
|
.contentRow{
|
|
23
33
|
position:relative;
|
|
@@ -7,7 +7,11 @@ export type UserMenuProps = {
|
|
|
7
7
|
theme?: ThemeProps;
|
|
8
8
|
/** Пункты после темы. На desktop закреплены сверху вместе с профилем и темой */
|
|
9
9
|
topItems?: DroplistProps['items'];
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Список организаций. На desktop — единственная прокручиваемая часть меню.
|
|
12
|
+
*
|
|
13
|
+
* Не передан или пуст — меню отображается единым списком без закреплённых частей.
|
|
14
|
+
*/
|
|
11
15
|
organizationItems?: DroplistProps['items'];
|
|
12
16
|
/** Пункты перед «Выйти из аккаунта». На desktop закреплены снизу */
|
|
13
17
|
bottomItems?: DroplistProps['items'];
|
|
@@ -49,6 +49,10 @@ function useUserMenuItems({ profile, theme, onLogout, topItems = emptyArray_1.EM
|
|
|
49
49
|
pinBottom: [],
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
+
// Без организаций прокручивать нечего: пустой `items` показал бы заглушку «Нет данных»
|
|
53
|
+
if (!organizationItems.length) {
|
|
54
|
+
return { pinTop: [], items: withClose([...pinTop, DIVIDER_ITEM, ...pinBottom]), pinBottom: [] };
|
|
55
|
+
}
|
|
52
56
|
return { pinTop: withClose(pinTop), items: withClose(organizationItems), pinBottom: withClose(pinBottom) };
|
|
53
57
|
}, [bottomItems, isMobile, logoutItem, onClose, organizationItems, profileItem, settingItems, themeItem, topItems]);
|
|
54
58
|
}
|
package/dist/cjs/style.css
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.breadcrums{
|
|
14
|
+
min-width:0;
|
|
14
15
|
padding:0 var(--sn-primitive-dimension-8, 8px);
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -116,6 +117,16 @@
|
|
|
116
117
|
.root:hover .closeButton, .root:focus-visible .closeButton, .root:has(*:focus-visible) .closeButton{
|
|
117
118
|
visibility:visible;
|
|
118
119
|
}
|
|
120
|
+
.root[data-mobile] .closeButton{
|
|
121
|
+
position:relative;
|
|
122
|
+
top:auto;
|
|
123
|
+
right:auto;
|
|
124
|
+
flex-shrink:0;
|
|
125
|
+
visibility:visible;
|
|
126
|
+
}
|
|
127
|
+
.root[data-mobile] .contentRow[data-closable]{
|
|
128
|
+
padding-right:var(--sn-primitive-dimension-4, 4px);
|
|
129
|
+
}
|
|
119
130
|
|
|
120
131
|
.contentRow{
|
|
121
132
|
position:relative;
|
|
@@ -6,15 +6,17 @@ import { PromoTag, ROLE_APPEARANCE, SIZE } from '@cloud-ru/ds-promo-tag';
|
|
|
6
6
|
import { Typography } from '@cloud-ru/ds-typography';
|
|
7
7
|
import cn from 'classnames';
|
|
8
8
|
import { useCallback } from 'react';
|
|
9
|
+
import { useMobileLayout } from '../../../../hooks/useMobileLayout.js';
|
|
9
10
|
import { headerLocale } from '../../../../locale/index.js';
|
|
10
11
|
import { MENU_BANNER_TEST_IDS } from './constants.js';
|
|
11
12
|
import styles from './styles.module.css';
|
|
12
13
|
export function MenuBanner({ title, promoTag, afterTitle, onClose, className, href = '#', 'data-test-id': dataTestId, ...rest }) {
|
|
13
14
|
const { t } = headerLocale.useTranslations();
|
|
15
|
+
const isMobile = useMobileLayout();
|
|
14
16
|
const handleClose = useCallback(e => {
|
|
15
17
|
e.preventDefault();
|
|
16
18
|
e.stopPropagation();
|
|
17
19
|
onClose?.(e);
|
|
18
20
|
}, [onClose]);
|
|
19
|
-
return (_jsx(Card, { as: 'a', href: href, radius: 'l', view: 'simple', className: cn(styles.root, className), "data-test-id": dataTestId ?? MENU_BANNER_TEST_IDS.root, ...rest, children: _jsxs("div", { className: styles.contentRow, children: [_jsxs("span", { className: styles.titleWrapper, children: [_jsx(Typography, { as: 'span', variant: 'body', size: 'm', className: styles.title, "data-test-id": MENU_BANNER_TEST_IDS.title, children: title }), promoTag && (_jsx(PromoTag, { ...promoTag, className: styles.promoTag, roleAppearance: ROLE_APPEARANCE.Decor, size: SIZE.Xs, "data-test-id": MENU_BANNER_TEST_IDS.promoTag }))] }), afterTitle && (_jsx("div", { className: styles.afterTitle, "data-test-id": MENU_BANNER_TEST_IDS.afterTitle, children: afterTitle })), onClose && (_jsx(Button, { className: styles.closeButton, view: 'elevated', appearance: 'neutral', size: 's', icon: _jsx(CrossSVG, {}), onClick: handleClose, "aria-label": t('close'), "data-test-id": MENU_BANNER_TEST_IDS.close }))] }) }));
|
|
21
|
+
return (_jsx(Card, { as: 'a', href: href, radius: 'l', view: 'simple', className: cn(styles.root, className), "data-mobile": isMobile || undefined, "data-test-id": dataTestId ?? MENU_BANNER_TEST_IDS.root, ...rest, children: _jsxs("div", { className: styles.contentRow, "data-closable": Boolean(onClose) || undefined, children: [_jsxs("span", { className: styles.titleWrapper, children: [_jsx(Typography, { as: 'span', variant: 'body', size: 'm', className: styles.title, "data-test-id": MENU_BANNER_TEST_IDS.title, children: title }), promoTag && (_jsx(PromoTag, { ...promoTag, className: styles.promoTag, roleAppearance: ROLE_APPEARANCE.Decor, size: SIZE.Xs, "data-test-id": MENU_BANNER_TEST_IDS.promoTag }))] }), afterTitle && (_jsx("div", { className: styles.afterTitle, "data-test-id": MENU_BANNER_TEST_IDS.afterTitle, children: afterTitle })), onClose && (_jsx(Button, { className: styles.closeButton, view: 'elevated', appearance: 'neutral', size: 's', icon: _jsx(CrossSVG, {}), onClick: handleClose, "aria-label": t('close'), "data-test-id": MENU_BANNER_TEST_IDS.close }))] }) }));
|
|
20
22
|
}
|
|
@@ -18,6 +18,16 @@
|
|
|
18
18
|
.root:hover .closeButton, .root:focus-visible .closeButton, .root:has(*:focus-visible) .closeButton{
|
|
19
19
|
visibility:visible;
|
|
20
20
|
}
|
|
21
|
+
.root[data-mobile] .closeButton{
|
|
22
|
+
position:relative;
|
|
23
|
+
top:auto;
|
|
24
|
+
right:auto;
|
|
25
|
+
flex-shrink:0;
|
|
26
|
+
visibility:visible;
|
|
27
|
+
}
|
|
28
|
+
.root[data-mobile] .contentRow[data-closable]{
|
|
29
|
+
padding-right:var(--sn-primitive-dimension-4, 4px);
|
|
30
|
+
}
|
|
21
31
|
|
|
22
32
|
.contentRow{
|
|
23
33
|
position:relative;
|
|
@@ -7,7 +7,11 @@ export type UserMenuProps = {
|
|
|
7
7
|
theme?: ThemeProps;
|
|
8
8
|
/** Пункты после темы. На desktop закреплены сверху вместе с профилем и темой */
|
|
9
9
|
topItems?: DroplistProps['items'];
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Список организаций. На desktop — единственная прокручиваемая часть меню.
|
|
12
|
+
*
|
|
13
|
+
* Не передан или пуст — меню отображается единым списком без закреплённых частей.
|
|
14
|
+
*/
|
|
11
15
|
organizationItems?: DroplistProps['items'];
|
|
12
16
|
/** Пункты перед «Выйти из аккаунта». На desktop закреплены снизу */
|
|
13
17
|
bottomItems?: DroplistProps['items'];
|
|
@@ -46,6 +46,10 @@ export function useUserMenuItems({ profile, theme, onLogout, topItems = EMPTY_AR
|
|
|
46
46
|
pinBottom: [],
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
+
// Без организаций прокручивать нечего: пустой `items` показал бы заглушку «Нет данных»
|
|
50
|
+
if (!organizationItems.length) {
|
|
51
|
+
return { pinTop: [], items: withClose([...pinTop, DIVIDER_ITEM, ...pinBottom]), pinBottom: [] };
|
|
52
|
+
}
|
|
49
53
|
return { pinTop: withClose(pinTop), items: withClose(organizationItems), pinBottom: withClose(pinBottom) };
|
|
50
54
|
}, [bottomItems, isMobile, logoutItem, onClose, organizationItems, profileItem, settingItems, themeItem, topItems]);
|
|
51
55
|
}
|
package/dist/esm/style.css
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.breadcrums{
|
|
14
|
+
min-width:0;
|
|
14
15
|
padding:0 var(--sn-primitive-dimension-8, 8px);
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -116,6 +117,16 @@
|
|
|
116
117
|
.root:hover .closeButton, .root:focus-visible .closeButton, .root:has(*:focus-visible) .closeButton{
|
|
117
118
|
visibility:visible;
|
|
118
119
|
}
|
|
120
|
+
.root[data-mobile] .closeButton{
|
|
121
|
+
position:relative;
|
|
122
|
+
top:auto;
|
|
123
|
+
right:auto;
|
|
124
|
+
flex-shrink:0;
|
|
125
|
+
visibility:visible;
|
|
126
|
+
}
|
|
127
|
+
.root[data-mobile] .contentRow[data-closable]{
|
|
128
|
+
padding-right:var(--sn-primitive-dimension-4, 4px);
|
|
129
|
+
}
|
|
119
130
|
|
|
120
131
|
.contentRow{
|
|
121
132
|
position:relative;
|