@cloud-ru/uikit-product-site-header 0.4.8

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 (44) hide show
  1. package/CHANGELOG.md +358 -0
  2. package/LICENSE +201 -0
  3. package/README.md +8 -0
  4. package/package.json +55 -0
  5. package/src/components/HeaderItems/HeaderItem.tsx +123 -0
  6. package/src/components/HeaderItems/index.ts +2 -0
  7. package/src/components/HeaderItems/styles.module.scss +46 -0
  8. package/src/components/HeaderItems/types.ts +12 -0
  9. package/src/components/SiteHeaderBasic/SiteHeaderBasic.tsx +133 -0
  10. package/src/components/SiteHeaderBasic/index.ts +1 -0
  11. package/src/components/SiteHeaderBasic/styles.module.scss +72 -0
  12. package/src/components/SubHeader/SubHeader.tsx +51 -0
  13. package/src/components/SubHeader/index.ts +1 -0
  14. package/src/components/SubHeader/styles.module.scss +37 -0
  15. package/src/components/UserDetailsDropdown/UserDetailsDropdown.tsx +46 -0
  16. package/src/components/UserDetailsDropdown/index.ts +1 -0
  17. package/src/components/UserDetailsDropdown/styles.module.scss +14 -0
  18. package/src/components/UserDetailsInline/UserDetailsInline.tsx +34 -0
  19. package/src/components/UserDetailsInline/index.ts +1 -0
  20. package/src/components/UserDetailsInline/styles.module.scss +9 -0
  21. package/src/components/index.ts +5 -0
  22. package/src/helperComponents/ButtonBurger/ButtonBurger.tsx +16 -0
  23. package/src/helperComponents/ButtonBurger/index.ts +1 -0
  24. package/src/helperComponents/ButtonBurger/styles.module.scss +21 -0
  25. package/src/helperComponents/LinkItemHeader/LinkItemHeader.tsx +36 -0
  26. package/src/helperComponents/LinkItemHeader/index.ts +1 -0
  27. package/src/helperComponents/LinkItemHeader/styles.module.scss +65 -0
  28. package/src/helperComponents/LogoContent/LogoContent.tsx +34 -0
  29. package/src/helperComponents/LogoContent/index.ts +1 -0
  30. package/src/helperComponents/LogoContent/styles.module.scss +39 -0
  31. package/src/helperComponents/MobileMenu/MobileMenu.tsx +27 -0
  32. package/src/helperComponents/MobileMenu/index.ts +1 -0
  33. package/src/helperComponents/MobileMenu/styles.module.scss +8 -0
  34. package/src/helperComponents/MoreButton/MoreButton.tsx +59 -0
  35. package/src/helperComponents/MoreButton/index.ts +1 -0
  36. package/src/helperComponents/MoreButton/styles.module.scss +39 -0
  37. package/src/helperComponents/UserInfo/UserInfo.tsx +25 -0
  38. package/src/helperComponents/UserInfo/index.ts +1 -0
  39. package/src/helperComponents/UserInfo/styles.module.scss +16 -0
  40. package/src/helperComponents/index.ts +5 -0
  41. package/src/hooks/index.ts +2 -0
  42. package/src/hooks/useHeaderPosition.ts +52 -0
  43. package/src/hooks/useResizeObserver.ts +23 -0
  44. package/src/index.ts +1 -0
@@ -0,0 +1,123 @@
1
+ import cn from 'classnames';
2
+ import { useEffect, useRef, useState } from 'react';
3
+
4
+ import { LinkItemHeader, MoreButton } from '../../helperComponents';
5
+ import { useResizeObserver } from '../../hooks';
6
+ import styles from './styles.module.scss';
7
+ import { LinkItem } from './types';
8
+
9
+ export type HeaderItemProps = {
10
+ /** Список элементов для пунктов меню хэдера */
11
+ linkItems?: LinkItem[];
12
+ /** Флаг является ли сейчас мобильная версия */
13
+ isMobileTabletView?: boolean;
14
+ /** Id активного элемента списка меню */
15
+ activeLinkItemId?: string;
16
+ };
17
+
18
+ const GAP_ITEMS = 22;
19
+
20
+ const WIDTH_MORE_BUTTON = 24;
21
+
22
+ export function HeaderItems({ linkItems, isMobileTabletView, activeLinkItemId }: HeaderItemProps) {
23
+ const [visibleItems, setVisibleItems] = useState<LinkItem[]>([]);
24
+ const [hiddenItems, setHiddenItems] = useState<LinkItem[]>([]);
25
+
26
+ const hiddenRowElementRef = useRef<HTMLDivElement>(null);
27
+
28
+ const [firstItemElement, setFirstItemElement] = useState<HTMLElement | null>(null);
29
+ const itemsMapRef = useRef(new Map<LinkItem, HTMLElement | null>());
30
+
31
+ const { width: maxWidth } = useResizeObserver(hiddenRowElementRef.current);
32
+ const { width: firstVisibleItemWidth } = useResizeObserver(firstItemElement);
33
+
34
+ function setItemElement(item: LinkItem, index: number) {
35
+ return (itemElement: HTMLElement | null) => {
36
+ if (index === 0 && itemElement) {
37
+ setFirstItemElement(itemElement);
38
+ }
39
+
40
+ if (itemElement === null) {
41
+ itemsMapRef.current.delete(item);
42
+ } else {
43
+ itemsMapRef.current.set(item, itemElement);
44
+ }
45
+ };
46
+ }
47
+
48
+ useEffect(() => {
49
+ if (maxWidth < 1) {
50
+ return;
51
+ }
52
+
53
+ const newVisibleItems: LinkItem[] = [];
54
+ const newHiddenItems: LinkItem[] = [];
55
+ let isHidden = false;
56
+ let indexElement = 0;
57
+
58
+ let currentRowWidth = 0;
59
+
60
+ const size = itemsMapRef.current.size;
61
+
62
+ itemsMapRef.current.forEach((itemElement, itemRowItem) => {
63
+ const itemWidth = itemElement?.offsetWidth || 0;
64
+ const isLastElement = indexElement + 1 === size;
65
+ const sumRowWidth = currentRowWidth + itemWidth + (isLastElement ? 0 : GAP_ITEMS);
66
+
67
+ if (isHidden) {
68
+ newHiddenItems.push(itemRowItem);
69
+ } else {
70
+ if (maxWidth > sumRowWidth + (isLastElement ? 0 : WIDTH_MORE_BUTTON)) {
71
+ newVisibleItems.push(itemRowItem);
72
+ currentRowWidth = sumRowWidth;
73
+ } else {
74
+ newHiddenItems.push(itemRowItem);
75
+ isHidden = true;
76
+ }
77
+ }
78
+ indexElement++;
79
+ });
80
+
81
+ setVisibleItems(newVisibleItems);
82
+ setHiddenItems(newHiddenItems);
83
+ }, [linkItems, maxWidth, firstVisibleItemWidth]);
84
+
85
+ if (!linkItems || isMobileTabletView) return null;
86
+
87
+ const isVisibleEmpty = visibleItems.length === 0;
88
+
89
+ return (
90
+ <div className={styles.wrapper}>
91
+ <div className={cn(styles.linkItemsContainer, styles.hiddenRow)} ref={hiddenRowElementRef}>
92
+ {linkItems.map((linkItem, index) => (
93
+ <div key={linkItem.id} className={styles.lastItemContainer} ref={setItemElement(linkItem, index)}>
94
+ <LinkItemHeader
95
+ label={linkItem.label}
96
+ target={linkItem.target}
97
+ href={linkItem.href}
98
+ onClick={linkItem.onClick}
99
+ />
100
+ </div>
101
+ ))}
102
+ </div>
103
+ <div
104
+ className={cn(styles.linkItemsContainer, {
105
+ [styles.linkItemsFirstViewContainer]: isVisibleEmpty,
106
+ })}
107
+ >
108
+ {/*Добавлено для сайта, так как используется next и приходит html без расчета js*/}
109
+ {(isVisibleEmpty ? linkItems : visibleItems).map(linkItem => (
110
+ <div key={linkItem.id} className={styles.lastItemContainer}>
111
+ <LinkItemHeader
112
+ label={linkItem.label}
113
+ href={linkItem.href}
114
+ onClick={linkItem.onClick}
115
+ active={activeLinkItemId === linkItem.id}
116
+ />
117
+ </div>
118
+ ))}
119
+ {hiddenItems.length > 0 && <MoreButton linkItemsArray={hiddenItems} activeItemId={activeLinkItemId} />}
120
+ </div>
121
+ </div>
122
+ );
123
+ }
@@ -0,0 +1,2 @@
1
+ export * from './HeaderItem';
2
+ export * from './types';
@@ -0,0 +1,46 @@
1
+ .wrapper {
2
+ display: flex;
3
+ flex-direction: row;
4
+ justify-content: start;
5
+ gap: 22px;
6
+ flex-wrap: nowrap;
7
+ min-height: 24px;
8
+ }
9
+
10
+ .linkItemsContainer {
11
+ display: flex;
12
+ flex-direction: row;
13
+ align-items: center;
14
+ flex-wrap: wrap;
15
+ gap: 22px;
16
+ }
17
+
18
+ .lastItemContainer {
19
+ display: flex;
20
+ flex-direction: row;
21
+ align-items: center;
22
+ flex-wrap: nowrap;
23
+ gap: 22px;
24
+ }
25
+
26
+ .hiddenRow {
27
+ position: absolute;
28
+ top: 0;
29
+ left: 0;
30
+
31
+ display: flex;
32
+ width: 100%;
33
+
34
+ visibility: hidden;
35
+ }
36
+
37
+ .linkItemsFirstViewContainer {
38
+ display: flex;
39
+ flex-direction: row;
40
+ overflow: hidden;
41
+ height: 24px;
42
+ flex-grow: 1;
43
+ align-items: center;
44
+ flex-wrap: wrap;
45
+ gap: 22px;
46
+ }
@@ -0,0 +1,12 @@
1
+ export type LinkItem = {
2
+ /** id Элемента */
3
+ id: string;
4
+ /** Текст элемента */
5
+ label: string;
6
+ /** Хэндлер клика элемента */
7
+ onClick?: () => void;
8
+ /** Ссылка элемента */
9
+ href?: string;
10
+ /** target для ссылки элемента */
11
+ target?: string;
12
+ };
@@ -0,0 +1,133 @@
1
+ import cn from 'classnames';
2
+ import { MouseEvent, ReactNode, useRef } from 'react';
3
+
4
+ import { Layout } from '@cloud-ru/uikit-product-site-layout';
5
+ import { extractSupportProps, WithLayoutType, WithSupportProps } from '@cloud-ru/uikit-product-utils';
6
+
7
+ import { ButtonBurger, LogoContent, MobileMenu } from '../../helperComponents';
8
+ import { useHeaderPosition } from '../../hooks';
9
+ import styles from './styles.module.scss';
10
+
11
+ export type AdditionalLogoText = {
12
+ /** Дополнительный текст Логотипа */
13
+ text?: string;
14
+ /** Переход по ссылке по дополнительному тексту Логотипа */
15
+ link?: string;
16
+ /** Коллбэк по клику на дополнительный текс Логотипа */
17
+ onClick?(event?: MouseEvent<HTMLAnchorElement>): void;
18
+ };
19
+
20
+ export type Logo = {
21
+ /** Переход по ссылке Логотипа */
22
+ logoLink: string;
23
+ /** Коллбэк по клику на Логотип */
24
+ onClick?(event?: MouseEvent<HTMLAnchorElement>): void;
25
+ };
26
+
27
+ export type HeaderProps = WithSupportProps<
28
+ WithLayoutType<{
29
+ /** Настройки текста справа Логотипа */
30
+ additionalLogoText?: AdditionalLogoText;
31
+ /** className root блока */
32
+ className?: string;
33
+ /** className основного хедера */
34
+ mainHeaderClassName?: string;
35
+ /** максимальная ширина контейнера */
36
+ maxWidth?: number;
37
+ /** Флаг открытия мобильного меню */
38
+ mobileMenuOpen: boolean;
39
+ /** Настройки Логотипа */
40
+ logo: Logo;
41
+ /** Функция изменения флаг открытия мобильного меню */
42
+ onSetMobileMenuOpen(open: boolean): void;
43
+ /** Контент посередине (между логотипом и правым блоком) */
44
+ middleContent?: ReactNode;
45
+ /** Контент занимающий всю возможную ширину хэдера */
46
+ fullWidthContent?: ReactNode;
47
+ /** Контент справа (левее бургера) */
48
+ rightContent?: ReactNode;
49
+ /** Контент сверху над хэдером, для инфостроки */
50
+ subHeader?: ReactNode;
51
+ /** Контент мобильной версии меню */
52
+ mobileMenuContent?: ReactNode;
53
+ /** Нижний контент кнопок мобильной версии меню */
54
+ mobileConsultationButton?: ReactNode;
55
+ }>
56
+ >;
57
+
58
+ const HEIGHT_SUBHEADER = 25;
59
+
60
+ export function SiteHeaderBasic({
61
+ className,
62
+ maxWidth,
63
+ additionalLogoText,
64
+ middleContent,
65
+ rightContent,
66
+ mobileMenuContent,
67
+ subHeader,
68
+ fullWidthContent,
69
+ layoutType,
70
+ mobileConsultationButton,
71
+ mobileMenuOpen,
72
+ onSetMobileMenuOpen,
73
+ mainHeaderClassName,
74
+ logo,
75
+ ...rest
76
+ }: HeaderProps) {
77
+ const refHeader = useRef<HTMLDivElement>(null);
78
+ const { showHeader, headerHeight } = useHeaderPosition(mobileMenuOpen, refHeader);
79
+
80
+ const isMobileTabletView = layoutType === 'mobile' || layoutType === 'tablet';
81
+ const isMobile = layoutType === 'mobile';
82
+
83
+ return (
84
+ <Layout.Header
85
+ style={{
86
+ transform: `translateY(-${!showHeader ? headerHeight + HEIGHT_SUBHEADER : 0}px)`,
87
+ }}
88
+ className={cn(styles.root, className)}
89
+ data-attr='layout-header'
90
+ {...extractSupportProps(rest)}
91
+ >
92
+ {subHeader}
93
+ <div
94
+ ref={refHeader}
95
+ className={cn(styles.headerMaster, styles.dividerHeader, mainHeaderClassName)}
96
+ data-layout-type={layoutType}
97
+ >
98
+ <div
99
+ className={styles.headerPartsContainer}
100
+ style={{
101
+ maxWidth,
102
+ }}
103
+ >
104
+ {fullWidthContent ?? (
105
+ <>
106
+ <div className={styles.leftPart} data-layout-type={layoutType}>
107
+ <LogoContent additionalLogoText={additionalLogoText} isMobile={isMobile} logo={logo} />
108
+ </div>
109
+ <div className={styles.middlePart}>{middleContent}</div>
110
+ <div className={styles.rightPart}>
111
+ {rightContent}
112
+ {mobileMenuContent && isMobileTabletView && (
113
+ <>
114
+ <ButtonBurger
115
+ mobileMenuOpen={mobileMenuOpen}
116
+ onClick={() => onSetMobileMenuOpen(!mobileMenuOpen)}
117
+ />
118
+ <MobileMenu
119
+ mobileConsultationButton={mobileConsultationButton}
120
+ mobileMenuContent={mobileMenuContent}
121
+ mobileMenuOpen={mobileMenuOpen}
122
+ onClickForCloseMobileMenu={() => onSetMobileMenuOpen(false)}
123
+ />
124
+ </>
125
+ )}
126
+ </div>
127
+ </>
128
+ )}
129
+ </div>
130
+ </div>
131
+ </Layout.Header>
132
+ );
133
+ }
@@ -0,0 +1 @@
1
+ export * from './SiteHeaderBasic';
@@ -0,0 +1,72 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as ste;
2
+
3
+ .root {
4
+ position: sticky;
5
+ top: 0;
6
+ transition:
7
+ box-shadow 0.3s ease-in-out,
8
+ transform 0.3s ease-in-out;
9
+ }
10
+
11
+ .headerMaster {
12
+ width: 100%;
13
+ background: ste.$sys-neutral-background1-level;
14
+ padding: 0 32px;
15
+
16
+ &[data-layout-type='mobile'] {
17
+ padding: 0 16px;
18
+ }
19
+ }
20
+
21
+ .dividerHeader {
22
+ box-sizing: border-box;
23
+ border-bottom: 1px solid ste.$sys-neutral-decor-default;
24
+ }
25
+
26
+ .leftPart {
27
+ display: flex;
28
+ flex-direction: row;
29
+ align-items: center;
30
+ margin-right: 40px;
31
+
32
+ &[data-layout-type='mobile'] {
33
+ margin-right: 0;
34
+ }
35
+ }
36
+
37
+ .middlePart {
38
+ position: relative;
39
+ display: flex;
40
+ flex-direction: row;
41
+ align-items: center;
42
+ flex-grow: 1;
43
+ justify-content: start;
44
+ gap: 40px;
45
+ }
46
+
47
+ .linkItemsContainer {
48
+ display: flex;
49
+ flex-direction: row;
50
+ align-items: center;
51
+ gap: 20px;
52
+ }
53
+
54
+ .rightPart {
55
+ display: flex;
56
+ flex-direction: row;
57
+ align-items: center;
58
+ gap: 12px;
59
+ }
60
+
61
+ .headerPartsContainer {
62
+ max-width: 1216px;
63
+ width: 100%;
64
+ display: flex;
65
+ align-items: center;
66
+ flex-direction: row;
67
+ justify-content: space-between;
68
+ height: 60px;
69
+ box-sizing: border-box;
70
+ margin: 0 auto;
71
+ overflow: hidden;
72
+ }
@@ -0,0 +1,51 @@
1
+ import cn from 'classnames';
2
+
3
+ import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
4
+ import { AlertTop, AlertTopProps } from '@snack-uikit/alert';
5
+
6
+ import styles from './styles.module.scss';
7
+
8
+ type BannerInfo = {
9
+ /** Цвет фона SubHeader */
10
+ color: 'yellow' | 'blue' | 'green';
11
+ /** Ссылка на текст SubHeader */
12
+ link?: string;
13
+ /** Текст SubHeader, может передаваться без ссылки */
14
+ title: string;
15
+ };
16
+
17
+ const APPEARANCE_ALERT: Record<BannerInfo['color'], AlertTopProps['appearance']> = {
18
+ green: 'success',
19
+ yellow: 'warning',
20
+ blue: 'info',
21
+ };
22
+
23
+ type SubHeaderProps = WithLayoutType<{
24
+ /** Объект для отображения данных на баннере */
25
+ bannerInfo: BannerInfo;
26
+ /** Функция закрытия SubHeader */
27
+ onCloseSubHeader?(): void;
28
+ }>;
29
+
30
+ export function SubHeader({ bannerInfo, onCloseSubHeader, layoutType }: SubHeaderProps) {
31
+ return (
32
+ <div className={cn(styles.root, styles[bannerInfo.color])} data-layout-type={layoutType}>
33
+ <AlertTop
34
+ className={styles.subHeaderContainer}
35
+ title={!bannerInfo.link ? bannerInfo.title : undefined}
36
+ link={
37
+ bannerInfo.link
38
+ ? {
39
+ text: bannerInfo.title,
40
+ href: bannerInfo.link,
41
+ }
42
+ : undefined
43
+ }
44
+ description=''
45
+ icon={false}
46
+ appearance={APPEARANCE_ALERT[bannerInfo.color]}
47
+ onClose={onCloseSubHeader}
48
+ />
49
+ </div>
50
+ );
51
+ }
@@ -0,0 +1 @@
1
+ export * from './SubHeader';
@@ -0,0 +1,37 @@
1
+ @use '@sbercloud/figma-tokens-web/build/scss/styles-theme-variables' as ste;
2
+
3
+ .root {
4
+ background: ste.$sys-neutral-background1-level;
5
+ padding: 0 16px;
6
+
7
+ &[data-layout-type='mobile'] {
8
+ padding: 0;
9
+ }
10
+ }
11
+
12
+ .subHeaderContainer {
13
+ max-width: 1248px;
14
+ margin: 0 auto;
15
+ height: 36px;
16
+ }
17
+
18
+ .closeIcon {
19
+ cursor: pointer;
20
+ }
21
+
22
+ .green {
23
+ background-color: ste.$sys-green-accent-default;
24
+ }
25
+
26
+ .yellow {
27
+ background-color: ste.$sys-yellow-accent-default;
28
+ }
29
+
30
+ .blue {
31
+ background-color: ste.$sys-blue-accent-default;
32
+ }
33
+
34
+ .link {
35
+ text-decoration: underline;
36
+ color: ste.$sys-graphite-text-main;
37
+ }
@@ -0,0 +1,46 @@
1
+ import { Avatar } from '@snack-uikit/avatar';
2
+ import { Dropdown } from '@snack-uikit/dropdown';
3
+
4
+ import { UserDetailsInline } from '../UserDetailsInline';
5
+ import styles from './styles.module.scss';
6
+
7
+ export type UserDetailsDropdownProps = {
8
+ userName: string;
9
+ lastName: string;
10
+ onClickExit: () => void;
11
+ onClickDropdownContent?: () => void;
12
+ };
13
+
14
+ export function UserDetailsDropdown({
15
+ userName,
16
+ lastName,
17
+ onClickExit,
18
+ onClickDropdownContent,
19
+ }: UserDetailsDropdownProps) {
20
+ return (
21
+ <Dropdown
22
+ offset={8}
23
+ placement='bottom-end'
24
+ onOpenChange={onClickDropdownContent}
25
+ content={
26
+ <div className={styles.wrapper}>
27
+ <UserDetailsInline
28
+ className={styles.container}
29
+ userName={userName}
30
+ lastName={lastName}
31
+ onClickExit={onClickExit}
32
+ withDivider
33
+ />
34
+ </div>
35
+ }
36
+ >
37
+ <Avatar
38
+ name={`${userName} ${lastName}`}
39
+ size='s'
40
+ showTwoSymbols
41
+ className={styles.authAvatar}
42
+ appearance='green'
43
+ />
44
+ </Dropdown>
45
+ );
46
+ }
@@ -0,0 +1 @@
1
+ export * from './UserDetailsDropdown';
@@ -0,0 +1,14 @@
1
+ .authAvatar {
2
+ cursor: pointer;
3
+ }
4
+
5
+ .wrapper {
6
+ min-width: 240px;
7
+ padding: 4px 8px;
8
+ /* stylelint-disable-next-line declaration-property-value-allowed-list */
9
+ z-index: 5;
10
+ }
11
+
12
+ .container {
13
+ gap: 4px;
14
+ }
@@ -0,0 +1,34 @@
1
+ import cn from 'classnames';
2
+
3
+ import { ExitSVG } from '@cloud-ru/uikit-product-icons';
4
+ import { ButtonFunction } from '@snack-uikit/button';
5
+ import { Divider } from '@snack-uikit/divider';
6
+
7
+ import { UserInfo } from '../../helperComponents/UserInfo';
8
+ import styles from './styles.module.scss';
9
+
10
+ export type UserDetailsInlineProps = {
11
+ userName: string;
12
+ lastName: string;
13
+ className?: string;
14
+ onClickExit: () => void;
15
+ withDivider?: boolean;
16
+ };
17
+
18
+ export function UserDetailsInline({ userName, lastName, onClickExit, withDivider, className }: UserDetailsInlineProps) {
19
+ return (
20
+ <div className={cn(styles.userAuthContent, className)}>
21
+ <UserInfo userName={userName} lastName={lastName} />
22
+ {withDivider && <Divider />}
23
+ <ButtonFunction
24
+ label='Выйти из аккаунта'
25
+ size='xs'
26
+ appearance='neutral'
27
+ icon={<ExitSVG size={24} />}
28
+ iconPosition='before'
29
+ className={styles.buttonExit}
30
+ onClick={onClickExit}
31
+ />
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1 @@
1
+ export * from './UserDetailsInline';
@@ -0,0 +1,9 @@
1
+ @use '@sbercloud/figma-tokens-web/build/scss/components/styles-tokens-element'
2
+ as ste;
3
+
4
+ .userAuthContent {
5
+ display: flex;
6
+ align-items: start;
7
+ flex-direction: column;
8
+ gap: 8px;
9
+ }
@@ -0,0 +1,5 @@
1
+ export * from './SiteHeaderBasic';
2
+ export * from './HeaderItems';
3
+ export * from './SubHeader';
4
+ export * from './UserDetailsInline';
5
+ export * from './UserDetailsDropdown';
@@ -0,0 +1,16 @@
1
+ import { BurgerSVG, CloseSVG } from '@cloud-ru/uikit-product-icons';
2
+
3
+ import styles from './styles.module.scss';
4
+
5
+ type ButtonBurgerProps = {
6
+ mobileMenuOpen: boolean;
7
+ onClick: () => void;
8
+ };
9
+
10
+ export function ButtonBurger({ mobileMenuOpen, onClick }: ButtonBurgerProps) {
11
+ return (
12
+ <button className={styles.buttonBurger} onClick={onClick}>
13
+ {mobileMenuOpen ? <CloseSVG /> : <BurgerSVG />}
14
+ </button>
15
+ );
16
+ }
@@ -0,0 +1 @@
1
+ export * from './ButtonBurger';
@@ -0,0 +1,21 @@
1
+ @use '@sbercloud/figma-tokens-web/build/scss/components/styles-tokens-element' as ste;
2
+
3
+ .buttonBurger {
4
+ background-color: transparent;
5
+ cursor: pointer;
6
+ box-shadow: none;
7
+ padding: 0;
8
+ height: 32px;
9
+ width: 32px;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ box-sizing: border-box;
14
+ border: 2px solid ste.$sys-graphite-decor-default;
15
+ color: ste.$sys-graphite-accent-default;
16
+
17
+ &:active,
18
+ &:focus {
19
+ outline: none;
20
+ }
21
+ }
@@ -0,0 +1,36 @@
1
+ import cn from 'classnames';
2
+
3
+ import { Typography } from '@snack-uikit/typography';
4
+
5
+ import styles from './styles.module.scss';
6
+
7
+ type LinkItemHeaderProps = {
8
+ href?: string;
9
+ target?: string;
10
+ label: string;
11
+ withoutHover?: boolean;
12
+ active?: boolean;
13
+ onClick?: () => void;
14
+ };
15
+
16
+ export function LinkItemHeader({ href, label, onClick, withoutHover, target, active }: LinkItemHeaderProps) {
17
+ if (href) {
18
+ return (
19
+ <a href={href} target={target} onClick={onClick} className={styles.item}>
20
+ <Typography.SansTitleS>{label}</Typography.SansTitleS>
21
+ </a>
22
+ );
23
+ }
24
+
25
+ return (
26
+ <button
27
+ onClick={onClick}
28
+ className={cn(styles.item, styles.button, {
29
+ [styles.active]: active,
30
+ [styles.hovered]: !withoutHover && !active,
31
+ })}
32
+ >
33
+ <Typography.SansTitleS>{label}</Typography.SansTitleS>
34
+ </button>
35
+ );
36
+ }