@cloud-ru/ds-uikit-product-header 2.0.2 → 2.0.3-preview-1207a902.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 (33) hide show
  1. package/dist/cjs/components/MainMenu/helperComponents/MenuBottom/MenuBottom.js +1 -1
  2. package/dist/cjs/components/MainMenu/helperComponents/MenuMobile/MenuMobile.js +2 -1
  3. package/dist/cjs/components/MainMenu/helperComponents/ScrollWithAnimatedStickyPanel/ScrollWithAnimatedStickyPanel.js +18 -6
  4. package/dist/cjs/components/UserMenu/UserMenu.js +5 -4
  5. package/dist/cjs/components/UserMenu/hooks/useLogoutItem.d.ts +2 -2
  6. package/dist/cjs/components/UserMenu/hooks/useLogoutItem.js +12 -9
  7. package/dist/cjs/components/UserMenu/hooks/useUserMenuItems.js +3 -3
  8. package/dist/cjs/components/UserMenu/styles.module.css +9 -0
  9. package/dist/cjs/constants.d.ts +0 -1
  10. package/dist/cjs/constants.js +1 -3
  11. package/dist/cjs/style.css +9 -0
  12. package/dist/esm/components/MainMenu/helperComponents/MenuBottom/MenuBottom.js +2 -2
  13. package/dist/esm/components/MainMenu/helperComponents/MenuMobile/MenuMobile.js +3 -2
  14. package/dist/esm/components/MainMenu/helperComponents/ScrollWithAnimatedStickyPanel/ScrollWithAnimatedStickyPanel.js +18 -6
  15. package/dist/esm/components/UserMenu/UserMenu.js +8 -7
  16. package/dist/esm/components/UserMenu/hooks/useLogoutItem.d.ts +2 -2
  17. package/dist/esm/components/UserMenu/hooks/useLogoutItem.js +9 -9
  18. package/dist/esm/components/UserMenu/hooks/useUserMenuItems.js +3 -3
  19. package/dist/esm/components/UserMenu/styles.module.css +9 -0
  20. package/dist/esm/constants.d.ts +0 -1
  21. package/dist/esm/constants.js +0 -2
  22. package/dist/esm/style.css +9 -0
  23. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  24. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  25. package/package.json +28 -28
  26. package/src/components/MainMenu/helperComponents/MenuBottom/MenuBottom.tsx +2 -2
  27. package/src/components/MainMenu/helperComponents/MenuMobile/MenuMobile.tsx +3 -2
  28. package/src/components/MainMenu/helperComponents/ScrollWithAnimatedStickyPanel/ScrollWithAnimatedStickyPanel.tsx +21 -10
  29. package/src/components/UserMenu/UserMenu.tsx +12 -7
  30. package/src/components/UserMenu/hooks/useLogoutItem.tsx +12 -12
  31. package/src/components/UserMenu/hooks/useUserMenuItems.ts +4 -4
  32. package/src/components/UserMenu/styles.module.scss +11 -0
  33. package/src/constants.ts +0 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-ru/ds-uikit-product-header",
3
- "version": "2.0.2",
3
+ "version": "2.0.3-preview-1207a902.0",
4
4
  "description": "Продуктовая шапка консоли — HeaderLayout, MainMenu, UserMenu, Logo, PathBreadcrumbs и PlatformLogo.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -45,33 +45,33 @@
45
45
  "@dnd-kit/utilities": "^3.2.2",
46
46
  "classnames": "^2.5.1",
47
47
  "fuse.js": "^7.1.0",
48
- "@cloud-ru/ds-accordion": "^1.1.3",
49
- "@cloud-ru/ds-adaptive": "^1.0.2",
50
- "@cloud-ru/ds-avatar": "^1.0.8",
51
- "@cloud-ru/ds-breadcrumbs": "^1.0.22",
52
- "@cloud-ru/ds-button": "^1.0.4",
53
- "@cloud-ru/ds-divider": "^1.0.2",
54
- "@cloud-ru/ds-card": "^1.0.17",
55
- "@cloud-ru/ds-drag-and-drop": "^1.1.3",
56
- "@cloud-ru/ds-drawer": "^1.2.5",
57
- "@cloud-ru/ds-info-block": "^1.0.3",
58
- "@cloud-ru/ds-icons": "^1.3.0",
59
- "@cloud-ru/ds-list": "^2.2.5",
60
- "@cloud-ru/ds-locale": "^1.0.0",
61
- "@cloud-ru/ds-modal": "^1.1.6",
62
- "@cloud-ru/ds-portal-context": "^1.0.2",
63
- "@cloud-ru/ds-promo-tag": "^1.0.3",
64
- "@cloud-ru/ds-scroll": "^1.0.5",
65
- "@cloud-ru/ds-search": "^1.0.11",
66
- "@cloud-ru/ds-theme": "^2.0.0",
67
- "@cloud-ru/ds-segment-control": "^1.0.10",
68
- "@cloud-ru/ds-tooltip": "^1.0.8",
69
- "@cloud-ru/ds-skeleton": "^1.0.2",
70
- "@cloud-ru/ds-typography": "^1.0.0",
71
- "@cloud-ru/ds-uikit-product-switch-row": "^1.0.11",
72
- "@cloud-ru/ds-uikit-product-card-predefined": "^4.0.2",
73
- "@cloud-ru/ds-utils": "^1.2.0",
74
- "@cloud-ru/ds-uikit-product-title-clickable": "^1.0.13"
48
+ "@cloud-ru/ds-adaptive": "1.0.2",
49
+ "@cloud-ru/ds-accordion": "1.1.3",
50
+ "@cloud-ru/ds-avatar": "1.0.8",
51
+ "@cloud-ru/ds-breadcrumbs": "1.0.23-preview-1207a902.0",
52
+ "@cloud-ru/ds-button": "1.0.4",
53
+ "@cloud-ru/ds-card": "1.0.18-preview-1207a902.0",
54
+ "@cloud-ru/ds-drag-and-drop": "1.1.3",
55
+ "@cloud-ru/ds-divider": "1.0.2",
56
+ "@cloud-ru/ds-icons": "1.3.0",
57
+ "@cloud-ru/ds-info-block": "1.0.3",
58
+ "@cloud-ru/ds-list": "2.2.6-preview-1207a902.0",
59
+ "@cloud-ru/ds-locale": "1.0.0",
60
+ "@cloud-ru/ds-modal": "1.1.7-preview-1207a902.0",
61
+ "@cloud-ru/ds-portal-context": "1.0.2",
62
+ "@cloud-ru/ds-promo-tag": "1.0.3",
63
+ "@cloud-ru/ds-search": "1.0.11",
64
+ "@cloud-ru/ds-scroll": "1.0.5",
65
+ "@cloud-ru/ds-segment-control": "1.0.10",
66
+ "@cloud-ru/ds-drawer": "1.2.6-preview-1207a902.0",
67
+ "@cloud-ru/ds-skeleton": "1.0.2",
68
+ "@cloud-ru/ds-theme": "2.0.0",
69
+ "@cloud-ru/ds-tooltip": "1.0.8",
70
+ "@cloud-ru/ds-typography": "1.0.0",
71
+ "@cloud-ru/ds-uikit-product-switch-row": "1.0.11",
72
+ "@cloud-ru/ds-utils": "1.2.0",
73
+ "@cloud-ru/ds-uikit-product-card-predefined": "4.0.3-preview-1207a902.0",
74
+ "@cloud-ru/ds-uikit-product-title-clickable": "1.0.13"
75
75
  },
76
76
  "publishConfig": {
77
77
  "access": "public"
@@ -1,5 +1,5 @@
1
1
  import { Divider } from '@cloud-ru/ds-divider';
2
- import { CardServiceLight } from '@cloud-ru/ds-uikit-product-card-predefined';
2
+ import { CardNavigation } from '@cloud-ru/ds-uikit-product-card-predefined';
3
3
  import { Fragment, ReactNode } from 'react';
4
4
 
5
5
  import { TEST_IDS } from '../../../../constants';
@@ -23,7 +23,7 @@ export function MenuBottom({ settingItems, leftBottom }: MenuBottomProps) {
23
23
  <Fragment key={item.id}>
24
24
  {item.divider === 'before' && <Divider orientation='horizontal' className={styles.bottomDivider} />}
25
25
 
26
- <CardServiceLight
26
+ <CardNavigation
27
27
  {...(item.href ? { href: item.href, as: 'a' } : { as: 'button', type: 'button' })}
28
28
  title={item.label}
29
29
  icon={getLinkEmblem(item)}
@@ -1,7 +1,8 @@
1
1
  import { Divider } from '@cloud-ru/ds-divider';
2
+ import { SNAP_POINTS_PRESET } from '@cloud-ru/ds-list';
2
3
  import { useCallback, useMemo } from 'react';
3
4
 
4
- import { BOTTOM_SHEET_FULLSCREEN_SNAP_POINTS, TEST_IDS } from '../../../../constants';
5
+ import { TEST_IDS } from '../../../../constants';
5
6
  import { MobileDrawerCustom } from '../../../../mobileOverlays';
6
7
  import { MainMenuDndContext } from '../../hooks/useMainMenuDnd';
7
8
  import { useMenuItems } from '../../hooks/useMenuItems';
@@ -67,7 +68,7 @@ export function MenuMobile({
67
68
  swipeEnabled={false}
68
69
  data-test-id={TEST_IDS.mainMenu.drawerMobile}
69
70
  closeOnPopstate
70
- snapPoints={BOTTOM_SHEET_FULLSCREEN_SNAP_POINTS}
71
+ snapPoints={SNAP_POINTS_PRESET.full}
71
72
  disableMotions={true}
72
73
  >
73
74
  <ScrollWithAnimatedStickyPanel
@@ -7,6 +7,11 @@ type ScrollWithAnimatedStickyPanelProps = PropsWithChildren<{
7
7
  panel: ReactNode;
8
8
  }>;
9
9
 
10
+ // Упругая прокрутка у верхней границы (touch-скролл на мобильных не "упирается" жёстко в 0 —
11
+ // контент можно немного "оттянуть" дальше) даёт scrollTop, не совпадающий с реальным положением
12
+ // контента в этот момент. Диапазон запаса — единицы пикселей; двух хватает с большим запасом.
13
+ const TOP_OVERSCROLL_EPSILON_PX = 2;
14
+
10
15
  export function ScrollWithAnimatedStickyPanel({ panel, children }: ScrollWithAnimatedStickyPanelProps) {
11
16
  const [positions, setPositions] = useState({ panelTopShift: 0, containerScroll: 0 });
12
17
  const [panelHeight, setPanelHeight] = useState(0);
@@ -23,6 +28,14 @@ export function ScrollWithAnimatedStickyPanel({ panel, children }: ScrollWithAni
23
28
 
24
29
  setPanelHeight(panelHeight);
25
30
 
31
+ // У верхней границы панель всегда полностью видна — не доверяем накопленному diff. Иначе
32
+ // "лишний" ход упругой прокрутки у самого верха читается как скролл вниз и на возврате к
33
+ // началу списка панель может остаться скрытой, хотя пользователь как раз скроллит наверх.
34
+ if (target.scrollTop <= TOP_OVERSCROLL_EPSILON_PX) {
35
+ setPositions({ panelTopShift: 0, containerScroll: target.scrollTop });
36
+ return;
37
+ }
38
+
26
39
  setPositions(prev => {
27
40
  const diff = prev.containerScroll - target.scrollTop;
28
41
 
@@ -33,21 +46,19 @@ export function ScrollWithAnimatedStickyPanel({ panel, children }: ScrollWithAni
33
46
  });
34
47
  }, []);
35
48
 
49
+ const setPanelRef = useCallback((element: HTMLDivElement | null) => {
50
+ if (element) {
51
+ setPanelHeight(element.offsetHeight);
52
+ panelRef.current = element;
53
+ }
54
+ }, []);
55
+
36
56
  return (
37
57
  <div className={styles.container} style={{ '--snack-autohide-panel-height': `${panelHeight}px` }}>
38
58
  <Scroll barHideStrategy='never' overflow={{ x: 'hidden' }} onScroll={handleScroll}>
39
59
  {children}
40
60
  </Scroll>
41
- <div
42
- className={styles.panel}
43
- ref={element => {
44
- if (element) {
45
- setPanelHeight(element.offsetHeight);
46
- panelRef.current = element;
47
- }
48
- }}
49
- style={{ top: positions.panelTopShift }}
50
- >
61
+ <div className={styles.panel} ref={setPanelRef} style={{ top: positions.panelTopShift }}>
51
62
  {panel}
52
63
  </div>
53
64
  </div>
@@ -1,10 +1,10 @@
1
1
  import { Avatar } from '@cloud-ru/ds-avatar';
2
- import { BaseItemProps, Droplist, DroplistProps } from '@cloud-ru/ds-list';
2
+ import { BaseItemProps, Droplist, DroplistProps, SNAP_POINTS_PRESET } from '@cloud-ru/ds-list';
3
3
  import { useValueControl } from '@cloud-ru/ds-utils';
4
4
  import cn from 'classnames';
5
- import { useMemo } from 'react';
5
+ import { useCallback, useMemo } from 'react';
6
6
 
7
- import { BOTTOM_SHEET_FULLSCREEN_SNAP_POINTS, TEST_IDS } from '../../constants';
7
+ import { TEST_IDS } from '../../constants';
8
8
  import { useDesktopComfortClassName } from '../../hooks/useDesktopComfortClassName';
9
9
  import { useMobileLayout } from '../../hooks/useMobileLayout';
10
10
  import { headerLocale } from '../../locale';
@@ -77,6 +77,10 @@ export function UserMenu({
77
77
 
78
78
  const { fullName = '', inviteCount } = profile;
79
79
 
80
+ const handleClose = useCallback(() => {
81
+ setOpen(false);
82
+ }, [setOpen]);
83
+
80
84
  const {
81
85
  pinTop,
82
86
  items: menuItems,
@@ -89,9 +93,7 @@ export function UserMenu({
89
93
  organizationItems,
90
94
  bottomItems,
91
95
  settingItems,
92
- onClose: () => {
93
- setOpen(false);
94
- },
96
+ onClose: handleClose,
95
97
  onLogout,
96
98
  });
97
99
 
@@ -136,7 +138,10 @@ export function UserMenu({
136
138
  closeOnPopstate
137
139
  data-test-id={TEST_IDS.userMenu.root}
138
140
  label={t('user')}
139
- snapPoints={BOTTOM_SHEET_FULLSCREEN_SNAP_POINTS}
141
+ snapPoints={SNAP_POINTS_PRESET.full}
142
+ safeArea
143
+ withDividers={false}
144
+ triggerClassName={styles.userMenuDroplistTrigger}
140
145
  >
141
146
  {trigger}
142
147
  </Droplist>
@@ -1,23 +1,23 @@
1
1
  import { ExitSVG } from '@cloud-ru/ds-icons/interface/product';
2
- import { ListProps } from '@cloud-ru/ds-list';
2
+ import { BaseItem } from '@cloud-ru/ds-list';
3
3
  import { useMemo } from 'react';
4
4
 
5
5
  import { headerLocale } from '../../../locale';
6
+ import styles from '../styles.module.scss';
6
7
 
7
- export function useLogoutItem({ onLogout }: { onLogout?(): void }): ListProps['items'] {
8
+ export function useLogoutItem({ onLogout }: { onLogout?(): void }): BaseItem {
8
9
  const { t } = headerLocale.useTranslations();
9
10
 
10
- return useMemo<ListProps['items']>(
11
- () => [
12
- {
13
- beforeContent: <ExitSVG />,
14
- content: {
15
- label: t('logout'),
16
- },
17
- onClick: onLogout,
18
- 'data-test-id': 'header__user-menu__logout',
11
+ return useMemo<BaseItem>(
12
+ () => ({
13
+ beforeContent: <ExitSVG />,
14
+ content: {
15
+ label: t('logout'),
19
16
  },
20
- ],
17
+ className: styles.logoutItem,
18
+ onClick: onLogout,
19
+ 'data-test-id': 'header__user-menu__logout',
20
+ }),
21
21
  [onLogout, t],
22
22
  );
23
23
  }
@@ -79,12 +79,12 @@ export function useUserMenuItems({
79
79
 
80
80
  let pinBottom = bottomItems.concat(logoutItem);
81
81
 
82
- if (isMobile && Boolean(settingItems?.length)) {
83
- pinBottom = pinBottom.concat([DIVIDER_ITEM, ...settingItems]);
84
- }
85
-
86
82
  // В шторке прокручивается всё меню, закреплять части незачем; разделитель над организациями добавляем сами
87
83
  if (isMobile) {
84
+ if (settingItems?.length) {
85
+ pinBottom = [...bottomItems, ...settingItems, DIVIDER_ITEM, logoutItem];
86
+ }
87
+
88
88
  return {
89
89
  pinTop: [],
90
90
  items: withClose([...pinTop, DIVIDER_ITEM, ...organizationItems, ...pinBottom]),
@@ -1,5 +1,9 @@
1
1
  @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
2
 
3
+ .userMenuDroplistTrigger {
4
+ display: inline-flex;
5
+ }
6
+
3
7
  .userMenuDroplist {
4
8
  width: 256px; // нет токена ширины дропдауна
5
9
  }
@@ -8,3 +12,10 @@
8
12
  .profileSettingsIcon {
9
13
  color: base.$sn-theme-color-neutral-text;
10
14
  }
15
+
16
+ .logoutItem {
17
+ span,
18
+ svg {
19
+ color: base.$sn-theme-color-red-text;
20
+ }
21
+ }
package/src/constants.ts CHANGED
@@ -26,6 +26,3 @@ export const TEST_IDS = {
26
26
  root: 'header__breadcrumbs',
27
27
  },
28
28
  } as const;
29
-
30
- // Только mobile: в мастере bottomSheet панель открывается на всю высоту экрана.
31
- export const BOTTOM_SHEET_FULLSCREEN_SNAP_POINTS = [1];